教小师妹快速入门Mybatis,看这篇就够了

回复“面试”获取全套面试资料

本文主要内容:

传统JDBC

传统JDBC编码格式

public class DataBaseUtil {
    public static final String URL = "jdbc:mysql://localhost:3306/mblog";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

public static void main(String[] args) throws Exception {
        
        Class.forName("com.mysql.jdbc.Driver");
        //2. 
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.
        Statement stmt = conn.createStatement();
        //4.
        ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
        //如果有数据,rs.next()返回true
        while(rs.next()){
            System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));
        }
    }
}

上面代码中知识为了展示JDBC整个过程(异常和资源是简单粗暴的处理了,我们关注的点不在这两个)。

大致可以分为六个步骤:

  1. 加载驱动程序
  2. 获得数据库连接
  3. 创建一个Statement对象
  4. 操作数据库,实现增删改查
  5. 获取结果集
  6. 关闭资源

从使用层面来说,采用原生态的JDBC在项目中使用起来成本还是很高的。如果我们的项目中的业务相对比较复杂,数据库表也相对较多,各种操作数据库的增删改查的方法也会随之多起来,那么这样的代码重复次数会非常之多。

传统JDBC的问题

  • 创建数据库的连接存在大量的硬编码,
  • 执行statement时存在硬编码.
  • 频繁的开启和关闭数据库连接,会严重影响数据库的性能,浪费数据库的资源.
  • 存在大量的重复性编码

为了解决以上问题,就诞生了各种各样替换JDBC的产品。即就是ORM框架。

什么是ORM?

全称为Object Relational Mapping。对象-映射-关系型数据库。对象关系映射(,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。

ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。

我们的项目中是这样的:

比如说:Apache DbUtils、Spring JDBC、 Hibernate、Ibatis(Mybatis的前生)、Spring Data Jpa等等。

目前最为流行的MybatisSpring Data Jpa。本系列我们先讲解MybatisJpa后面再讲。

ORM的优缺点

优点1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。

缺点牺牲程序的执行效率和会固定思维模式,降低了开发的灵活性。

从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。

MyBatis 是什么?

如果在面试的时候被问到,只要你说出下面三种即可。

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

来自官网。

MyBatis的优点和缺点

优点:

(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;

(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。

(4)能够与Spring很好的集成;

(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

缺点

(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

Mybatis环境搭建及简单实例

创建一张数据库表

创建一张m_user表使用MySQL数据库。

CREATE TABLE `m_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

添加依赖

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
            <scope>runtime</scope>
        </dependency>
</dependencies>

项目结构如下:

创建一个mybatis-config.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/mblog?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

实体类User

ublic class User {
    private Integer id;
    private String name;
    private Integer age;
    //set get
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建UserMapper.java

import com.tian.mybatis.entity.User;

public interface UserMapper {
    User selectUserById(Integer id);
}

创建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.tian.mybatis.mapper.UserMapper">
    <select id="selectUserById" resultType="com.tian.mybatis.entity.User">
        select * from m_user where id = #{id}
    </select>
</mapper>

创建一个测试类

import com.tian.mybatis.entity.User;
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 java.io.IOException;
import java.io.InputStream;

public class MybatisApplication {

public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        SqlSession sqlSession =null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            //工厂模式
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //sql操作会话
            sqlSession = sqlSessionFactory.openSession();
            //获取数据并解析成User对象
            User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSession.close();
        }
    }

}

输出结果:

User{id=1, name='tian', age=22}

整体步骤:

另外一种启动方式

import com.tian.mybatis.entity.User;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class MybatisApplication {

public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //解析xml文件
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
        //构建一个SqlSessionFactory工厂类
        SqlSessionFactory sqlSessionFactory = build(parser.parse());
        //创建一个SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取数据并解析成User对象
        User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
        System.out.println(user);
    }

public static SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

输出和上面一样。还可以直接使用Java代码而不用mybatis-config.xml

//创建数据源
DataSource dataSource = getDataSource();

TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

小总结

从上面这个案例中我们大致能猜到,Mybatis中最主要几个组件:

SqlSessionFactoryBuilder

SqlSessionFactory

SqlSession

Mapper

SqlSessionFactoryBuilder这个类可以被初始、使用和丢弃,如果你已经创建好了一个 SqlSessionFactory后就不用再保留它。因此 ,SqlSessionFactoryBuilder 的最好作用域是方法体内,比如说定义一个方法变量。

你可以重复使用SqlSessionFactoryBuilder 生成多个 SqlSessionFactory 实例,但是最好不要强行保留,因为 XML 的解析资源要用来做其它更重要的事。

SqlSessionFactory一旦创建,SqlSessionFactory就会在整个应用过程中始终存在。所以没有理由去销毁和再创建它,一个应用运行中也不建议多次创建 SqlSessionFactory。如果真的那样做,会显得很拙劣。

因此 SqlSessionFactory最好的作用域是 Application。可以有多种方法实现。最简单的方法是单例模式或者是静态单例模式。然而这既不是广泛赞成和好用的。反而,使用 Google Guice 或 Spring 来进行依赖反射会更好。这些框架允许你生成管理器来管理 SqlSessionFactory的单例生命周期

SqlSession每个线程都有自己的 SqlSession 实例,SqlSession 实例是不能被共享,也是不是线程安全的。因此最好使用 Request 作用域或者方法体作用域。

不要使用类的静态变量来引用一个 SqlSession 实例,甚至不要使用类的一个实例变更来引用。永远不要在一个被管理域中引用 SqlSession,比如说在 Servlet 中的HttpSession 中。如果你正在使用 WEB 框架,应该让 SqlSession 跟随 HTTP 请求的相似作用域。

也就是说,在收到一个 HTTP 请求过后,打开 SqlSession,等返回一个回应以后,立马关掉这个 SqlSession。关闭 SqlSession 是非常重要的。你必须要确保 SqlSession 在 finally 方法体中正常关闭。

SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}

使用这种模式来贯穿你的所有代码,以确保所有数据库资源都被完全关闭。

Mapper

Mapper 是一种你创建的用于绑定映射语句的接口。Mapper 接口的实例是用 SqlSession 来获得的。同样 ,从技术上来说,最广泛的 Mapper 实例作用域像 SqlSession 一样,使用请求作用域。确切地说,在方法被调用的时候调用 Mapper 实例,然后使用后,就自动销毁掉。不需要使用明确的注销。当一个请求执行正确无误的时候,像 SqlSession 一样,你可以轻而易举地操控这一切。保持简单性,保持 Mapper 在方法体作用域内。

 //获取数据并解析成User对象
 User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
 System.out.println(user);

这里映射涉及到四个主体:

  1. 实体类User。
  2. 接口UaerMapper
  3. xml配置文件UserMapper
  4. 数据库表m_user

Mybatis的五部曲:

总结

回顾JDBC的demo,JDBC中的问题,创建一个Mybatis示例,总结出重要的四个组件,以及每个组件的作用。

推荐阅读

《亿级流量网站架构核心技术》.pdf
《算法的乐趣》.pdf
面试官:Object有些什么方法?教你如何吊打他
(0)

相关推荐