yaochow@home:~$

【介绍】Mybatis

框架

成熟稳健的框架作用是高效稳定的封装好开发中需要的基本操作,让开发人员可以专注于业务逻辑开发。

三层架构

目前比较成熟的后端开发架构基本上分为了三层,表现层、业务层、持久层。

JDBC

在最初没有框架时是直接通过JDBC与数据库进行交互的,实现方式如下:

package com.local.demo;

import org.junit.Test;

import java.sql.*;

/**
 * 直接通过JDBC来实现程序与数据库交互的方式,
 */
public class TestCase0 {
    @Test
    public void testFindOne() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            //加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //通过驱动获取数据库连接
            connection = DriverManager.getConnection(
                    "jdbc:mysql://xxxxx:3306/xxx?useUnicode=true&characterEncoding=utf8",
                    "xxx",
                    "xxx"
            );

            //编写sql语句
            String sql = "select * from user_dev where id = ?";
            //预处理sql
            preparedStatement = connection.prepareStatement(sql);
            //设置参数
            preparedStatement.setInt(1, 1);
            //获取结果集
            resultSet = preparedStatement.executeQuery();

            //遍历获取结果
            while (resultSet.next())
                System.out.println(resultSet.getString("user_name"));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

什么是Mybatis

Mybatis前身是Apache开源项目ibatis。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

Mybatis架构

Mybatis依赖

  1. 将mybatis-x.x.x.jar文件置于classpath中

  2. Maven项目在pom.xml中注入依赖

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

Mybatis相关配置文件

db.properties
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://xxx:3306/xxx?useUnicode=true&characterEncoding=utf8
db.username=xxx
db.password=xxx
Configuration.xml

可以看到,数据库的连接信息都配置到xml文件中,或者说配置到了xml中获取的db.properties中。

<?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"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

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.local.mapper.UserDevMapper">

    <resultMap id="userDevResultMap" type="com.local.entity.UserDev">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="user_name" jdbcType="VARCHAR" property="userName"/>
        <result column="age" jdbcType="INTEGER" property="age"/>
        <result column="gender" jdbcType="INTEGER" property="gender"/>
        <result column="gmt_created" jdbcType="TIMESTAMP" property="gmtCreated"/>
        <result column="creator" jdbcType="VARCHAR" property="creator"/>
        <result column="gmt_modified" jdbcType="TIMESTAMP" property="gmtModified"/>
        <result column="modifier" jdbcType="VARCHAR" property="modifier"/>
        <result column="is_deleted" jdbcType="VARCHAR" property="isDeleted"/>
    </resultMap>

    <select id="findUserById" parameterType="int" resultMap="userDevResultMap">
		select * from user_dev where id = #{id}
	</select>

    <insert id="insertOne" parameterType="com.local.entity.UserDev">
        insert into user_dev (
        user_name,
        age, gender,
        creator,
        gmt_created,
        modifier,
        gmt_modified,
        is_deleted
        ) values (
        #{userName},
        #{age},
        #{gender},
        #{creator},
        #{gmtCreated},
        #{modifier},
        #{gmtModified},
        #{isDeleted}
        )
    </insert>
</mapper>

Mybatis的第一个例子

package com.local.config;

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

import java.io.IOException;
import java.io.InputStream;

public class MybatisConfiguration {

    private static final String configPath = "Configuration.xml"; // 配置文件
    private static SqlSessionFactory sqlSessionFactory;

    static {
        init();
    }
    
    private MybatisConfiguration(){}

    private static class SqlSessionFactoryInner {
        private static final MybatisConfiguration INSTANCE = new MybatisConfiguration();
    }

    private static void init() {
        try {
            InputStream inputStream = Resources.getResourceAsStream(configPath);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() throws IOException {
        return SqlSessionFactoryInner.INSTANCE.sqlSessionFactory;
    }
}

工具类。

package com.local.demo;

import com.local.config.MybatisConfiguration;
import com.local.entity.UserDev;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import java.io.IOException;
import java.util.Date;

public class TestCase1 {
    @Test
    public void testFindOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDev userDev = sqlSession.selectOne("com.local.mapper.UserDevMapper.findUserById", 1);
        sqlSession.close();
        System.out.print(userDev == null ? null : userDev.getUserName());
    }

    @Test
    public void testAddOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDev userDev = getUserDev();
        int insert = sqlSession.insert("com.local.mapper.UserDevMapper.insertOne", userDev);
        sqlSession.commit();
        sqlSession.close();
        System.out.print(insert);
    }

    public static UserDev getUserDev() {
        UserDev userDev = new UserDev();
        userDev.setAge(11);
        userDev.setCreator("SYSTEM");
        userDev.setGender(1);
        userDev.setGmtCreated(new Date());
        userDev.setGmtModified(new Date());
        userDev.setIsDeleted("N");
        userDev.setModifier("SYSTEM");
        userDev.setUserName("YY");
        return userDev;
    }
}

初始化流程

可以看到对于开发人员来说整个流程是这样的:开始–>获取SqlSessionFactory–>通过SqlSessionFactory打开一个SqlSession–>通过SqlSession与数据库进行交互–>结束流程。

执行中是如何找到对应的sql的?

“com.local.mapper.UserDevMapper”是在UserMapper.xml中定义的namespace,再通过”findUserById”或者”insertOne”定位到对应的sql。

Mybatis Dao层

方式一

定义Dao层接口

package com.local.dao;

import com.local.entity.UserDev;

public interface UserDevDao {

    UserDev selectOneById(int id);

    int insertOne(UserDev userDev);
}

定义Dao层接口实现

package com.local.dao.impl;

import com.local.dao.UserDevDao;
import com.local.entity.UserDev;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

public class UserDevDaoImpl implements UserDevDao {
    private SqlSessionFactory sqlSessionFactory;

    public UserDev selectOneById(int id) {
        SqlSession session = sqlSessionFactory.openSession();
        UserDev userDev = null;
        try {
            userDev = session.selectOne("com.local.mapper.UserDevMapper.findUserById", id);
        } finally {
            session.close();
        }
        return userDev;

    }

    public int insertOne(UserDev userDev) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int result = 0;
        try {
            result = sqlSession.insert("com.local.mapper.UserDevMapper.insertOne", userDev);
            sqlSession.commit();
        } finally {
            sqlSession.close();
        }
        return result;
    }

    public UserDevDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

}

测试类

package com.local.demo;

import com.local.config.MybatisConfiguration;
import com.local.dao.UserDevDao;
import com.local.dao.impl.UserDevDaoImpl;
import com.local.entity.UserDev;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import java.io.IOException;

public class TestCase2 {
    @Test
    public void testFindOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        UserDevDao userDevDao = new UserDevDaoImpl(sqlSessionFactory);
        UserDev userDev = userDevDao.selectOneById(1);
        System.out.print(userDev == null ? null : userDev.getUserName());
    }

    @Test
    public void testAddOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        UserDevDao userDevDao = new UserDevDaoImpl(sqlSessionFactory);
        UserDev userDev = TestCase1.getUserDev();
        int insert = userDevDao.insertOne(userDev);
        System.out.print(insert);
    }
}

方式二

定义Dao(Mapper)层接口

package com.local.mapper;

import com.local.entity.UserDev;

public interface UserDevMapper {

    UserDev findUserById(int id);

    int insertOne(UserDev userDev);
}

测试类

package com.local.demo;

import com.local.config.MybatisConfiguration;
import com.local.entity.UserDev;
import com.local.mapper.UserDevMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import java.io.IOException;

public class TestCase3 {
    @Test
    public void testFindOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDevMapper mapper = sqlSession.getMapper(UserDevMapper.class);
        UserDev userDev = mapper.findUserById(1);
        sqlSession.close();
        System.out.print(userDev == null ? null : userDev.getUserName());
    }

    @Test
    public void testAddOne() throws IOException {
        SqlSessionFactory sqlSessionFactory = MybatisConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDevMapper mapper = sqlSession.getMapper(UserDevMapper.class);
        UserDev userDev = TestCase1.getUserDev();
        int insert = mapper.insertOne(userDev);
        sqlSession.commit();
        sqlSession.close();
        System.out.print(insert);
    }
}

配置文件细节

配置文件标签、Mapper配置文件标签、动态sql等就不说了。

缓存

Mybatis分为一级缓存(session)和二级缓存(全局)。

Mybatis逆向工程

所谓逆向工程就是通过数据库中创建的表生成对应的DO、DAO、Mapper等信息。

下一篇源码篇~