MyBatis入门——了解基本概念

原文链接:https://www.dubby.cn/detail.html?id=9093

1. 了解MyBatis

1.1 MyBatis是什么?

使用Java操作数据库的话,JDK给我们提供了一层对各个数据库的封装,也就是JDBC,它屏蔽了数据库之间的差异,使用JDBC可以统一操作。但是他长期以来被人诟病的就是重复代码太多。封装参数需要一个一个set,还需要你把ResultSet一个一个映射成POJO,这里面充满了重复代码,这里完全可以由模板代码来代替。当然,不仅仅是重复代码,还有其他缺点,比如,性能,安全,扩展性等等。

封装JDBC以求达到更简洁,更优雅,更安全,更高效的方案有很多,MyBatis就是其中之一,这类框架一般被叫做ORM框架(Object Relational Mapping,就是Java中的对象和关系型数据库里的数据映射的一个框架)。

MyBatis通过构造一个Mapper来消除JDBC代码,却又可以实现查询数据库的功能。Mapper是个interface,我们可以指定这个接口里每个方法对应的SQL模板,然后由MyBatis来绑定运行时传入的参数给SQL模板里的占位符,构造成一个可执行的SQL,然后执行,获得结果后再自动封装成方法返回值的类型的对象。

举个例子,Mapper

Blog selectBlog(long id);

SQL:

select * from Blog where id = #{id}

这样查询时的参数绑定,和查询结果的封装都可以由MyBatis来完成。

1.2 为什么使用MyBatis?

知道MyBatis是什么,应该就明白为什么会出现MyBatis之类的ORM框架了,还有常见的Hibernate。对于ORM框架,或者类似的东西,存在的意义就是上面所说的。那么,为什么是MyBatis?而不是其他ORM框架呢?这里我简单的说说,为什么我喜欢MyBatis胜过喜欢Hibernate吧,我更喜欢简洁。说通俗一点,ORM确实有存在的必要,但是真的有必要封装的太过深吗?Hiberante可以达到让用户完全不知道自己在使用数据库的程度,这实现起来肯定比MyBatis更复杂,说明Hibernate的技术含量要更高,可是这也带来了问题,我们真的需要这样吗?

过度的封装,会带来更多的成本,学习成本、故障解决时的成本。而相比Hibernate的高度复杂,MyBatis就显得很简小精悍了。我想这也是很多人喜欢用guice而不是spring吧。

这里随意比较一下两个框架的文档:


image
image

2. 基本概念

这里介绍一下MyBatis最核心的几个类,虽然框架帮我们隐藏了很多细节,但是如果对他不够了解,就不能应用的得心应手。这里介绍的几个类都很容易理解,名字起的好也是一种本领啊。

2.1 SqlSessionFactoryBuilder

SqlSessionFactoryBuilder是根据配置创建SqlSessionFactory的工厂类,一般来说是通过XML,当然,我们也可以直接用代码来构建。这个类一旦创建出SqlSessionFactory之后,他的意义就结束了,可以被释放了。一般推荐在方法内定义,用完就释放。当然,你可以用它创建多个不同的SqlSessionFactory

2.2 SqlSessionFactory

SqlSessionFactory一旦创建就应该一直存在,一般建议维护在应用级别(不是方法级别,也不是类级别,而是整个应用共享)。一般来说,不需要重复创建这个对象,也不要释放它。可以用单例模式来保证整个应用内的唯一性。

2.3 SqlSession

SqlSession是线程不安全的,所以每个线程应该维护一个单独的SqlSession对象。千万不要用对象的静态变量引用来指向一个SqlSession,这样会导致它泄露到其他线程中。而且,一定要记得关闭SqlSession,常见的使用方式如下:

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

2.4 Mapper Instances

Mapper Instances是我们定义的Mapper接口的实例对象,他包含了SQL绑定关系,需要SqlSession创建。所以他的生命周期可以和SqlSession一致,也就是线程级别,但是为了简洁,我还是建议放在方法内部,它不需要关闭,使用方法一般如下:

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

3. 基本使用

代码可直接去Github上下载:https://github.com/dubby1994/mybatis_study

依赖:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>6.0.6</version>
    </dependency>
</dependencies>

3.1 XML版(经典版本)

MyBatis的经典版本就是用XML来配置,并用XML来实现SQL和Java代码的分离。

创建数据库:

CREATE TABLE `blog` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(1024) DEFAULT NULL,
  `content` text,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;

插入数据:

INSERT INTO `blog` (`id`, `title`, `content`, `create_time`)
VALUES
    (1, '标题1', '内容1', '2018-02-05 10:45:26');

编写主配置文件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>
    <typeAliases>
        <package name="cn.dubby.study.mybatis.basic.xml.entity"/>
    </typeAliases>

    <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/mybatis_study?useUnicode=true&amp;characterEncoding=UTF-8&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"/>
                <property name="username" value="test"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="cn/dubby/study/mybatis/basic/xml/mapper/BlogMapper.xml"/>
    </mappers>
</configuration>

编写启动类Main.java

package cn.dubby.study.mybatis.basic.xml;

import cn.dubby.study.mybatis.basic.xml.entity.Blog;
import cn.dubby.study.mybatis.basic.xml.mapper.BlogMapper;
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 Main {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {
            Blog blog1 = session.selectOne("cn.dubby.study.mybatis.basic.xml.mapper.BlogMapper.selectBlog", 1);
            System.out.println(blog1);

            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog2 = mapper.selectBlog(1);
            System.out.println(blog2);
        } finally {
            session.close();
        }
    }
}

编写POJO Blog.java

package cn.dubby.study.mybatis.basic.xml.entity;

import java.util.Date;

public class Blog {

    private Long id;
    private String title;
    private String content;
    private Date createTime;

    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", createTime=" + createTime +
                '}';
    }
    
    //getter and setter ...
}

编写Mapper BlogMapper.java

package cn.dubby.study.mybatis.basic.xml.mapper;

import cn.dubby.study.mybatis.basic.xml.entity.Blog;

public interface BlogMapper {
    Blog selectBlog(long id);
}

运行Main.java结果:

Blog{id=1, title='标题1', content='内容1', createTime=Mon Feb 05 18:45:26 CST 2018}
Blog{id=1, title='标题1', content='内容1', createTime=Mon Feb 05 18:45:26 CST 2018}

3.2 注解版

可以做到完全没有XML,总共有三个类,Main.javaSerial.javaSerialMapper.java

先准备数据库:

CREATE TABLE `serial` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(1024) DEFAULT NULL,
  `description` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `serial` (`id`, `name`, `description`)
VALUES
    (1, 'MyBatis教程', '这一系列文章主要是介绍MyBatis,包含入门使用,高级特性,以及实现细节。');

Main.java

package cn.dubby.study.mybatis.basic.without.xml;

import cn.dubby.study.mybatis.basic.without.xml.entity.Serial;
import cn.dubby.study.mybatis.basic.without.xml.mapper.SerialMapper;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
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.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import javax.sql.DataSource;

public class Main {

    public static void main(String[] args) {
        DataSource dataSource = new UnpooledDataSource("com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3306/mybatis_study?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=UTC", "test", "123456");
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource);
        Configuration configuration = new Configuration(environment);
        configuration.addMapper(SerialMapper.class);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

        SqlSession session = sqlSessionFactory.openSession();
        try {
            Serial serial1 = session.selectOne("cn.dubby.study.mybatis.basic.without.xml.mapper.SerialMapper.selectSerial", 1);
            System.out.println(serial1);

            SerialMapper mapper = session.getMapper(SerialMapper.class);
            Serial serial2 = mapper.selectSerial(1);
            System.out.println(serial2);
        } finally {
            session.close();
        }
    }
}

Serial.java

package cn.dubby.study.mybatis.basic.without.xml.entity;

public class Serial {

    private Long id;
    private String name;
    private String description;

    @Override
    public String toString() {
        return "Serial{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
    
    //setter and getter...
}

SerialMapper.java

package cn.dubby.study.mybatis.basic.without.xml.mapper;

import cn.dubby.study.mybatis.basic.without.xml.entity.Serial;
import org.apache.ibatis.annotations.Select;

public interface SerialMapper {
    @Select("SELECT * FROM serial WHERE id = #{id}")
    Serial selectSerial(long id);
}

执行Main.java

Serial{id=1, name='MyBatis教程', description='这一系列文章主要是介绍MyBatis,包含入门使用,高级特性,以及实现细节。'}
Serial{id=1, name='MyBatis教程', description='这一系列文章主要是介绍MyBatis,包含入门使用,高级特性,以及实现细节。'}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容