IT技术篇

MyBatis易懂教程

2019-05-08  本文已影响0人  WJunF

MyBatis 教程


假设数据库中存在一张表 person,它有两个字段,分别是主键和名字(id, name):

-- 创建表 person

create table person (id number primary key, name varchar2(20));

-- 插入几条示例数据

insert into person values (1, '周星驰');

insert into person values (2, '张学友');

insert into person values (3, '王家卫');

commit;

站在 ORM 的角度考虑,我们应该为这个表创建一个类,Person,它跟 person 表是对应关系。

public class Person {

    private int id;

    private String name;

    // ...

}

如果按照 JDBC 的原生写法,我们从 person 表中

读取 id 为 1 的数据

删除 id 为 2 的数据

大致要这么写:

// 查询大约分下面几步

// 1 ... jdbc 的方式获取数据库连接 Connection

// 2 ... 将结果封装到 Person 类中

// 3 ... 清理数据库连接,做好异常的处理

ResultSet rs = conn.executeQuery("select * from person where id = 1");

Person person = new Person(rs.getLong(1), rs.getString(2));

// 删除类似

// 1 ... jdbc 的方式获取数据库连接 Connection

// 2 ... 执行删除

// 3 ... 处理事务,关闭资源,处理异常

conn.execute("delete from Person where id = 2");

每次这么写很麻烦,容易出错。所以出现了一些 ORM 框架,帮助我们进行数据库连接。

比如 hibernate。

它的思路是,在实体对象中设置好映射和关联后,我们只需要对对象进行各种操作(增删改查等)。 框架会帮助我们把这些操作翻译成对数据库的操作。 \ 上面的功能,用 hibernate 实现,大致的代码为:

// 1. 读取配置文件,建立 SessionFactory

SessionFactory sessionFactory = new Configuration("hibernate.xml").config().buildSessionFactory();

// 2. 获取一个连接(session)

Session session = sessionFactory.getSession();

// 3. 执行操作,查询、删除

// 这是面向对象的语法。hibernate 负责将我们的操作翻译成相对应的 jdbc 操作

// 这大大简化了我们的数据操作。

Person tom = session.get(Person.class, 1);

Person cat = session.get(Person.class, 2);

session.delete(cat);

session.save(cat);

hibernate 非常强大,在它的基础上,形成了 JavaEE 的 ORM 标准 JPA。 但是 hibernate 也存在一些缺点,比如:

相对比较重型,学习门槛高!!!

过于面向对象,过度封装。

虽然提升了开发效率,但是对执行效率有一定的牺牲(因为翻译成的 jdbc 操作未必是最优的)。所以,很难调优。

不够灵活。

所以,怎么兼顾 jdbc 和 hibernate 方式的优点,是很多人考虑的问题。于是 mybatis 出现了。

它的思路很简单,例如,我们要对 person 表进行操作(查询和删除),在 jdbc 中对应两条语句:

select id, name from person where id = 1;

delete from person where id = 2;

那是不是可以弄一个配置文件,为这两条语句分别取个名字呢? 下面示例 xml 里,为两条 sql 语句分别起了名字,叫 selectFromPerson 和 deleteFromPerson

<mapper>

  <sql name="selectFromPerson">select id, name from person where id = 1</sql>

  <sql name="deleteFromPerson">delete from person where id = 2</sql>

</mapper>

然后,我们可以这样调用:

// 初始化 mybatis,加载配置文件和数据库信息,封装到一个对象里,这里叫 SqlSessionFactory

SqlSessionfactory sqlsessionfactory = 初始化代码;

// 如果要查询

// 首先,mybatis 需要去寻找名字叫 "selectFromPerson" 的 sql 语句。

// 然后,通过我们给的数据库配置,它会帮我们连接数据库,执行上面查到的语句,封装结果到 Person 对象里。

// 所以,差不多是这样的代码:

Person de = (Person) sqlsessionfactory.getSession().exec("selectFromPerson");

// 如果要删除,也是类似的逻辑

sqlsessionfactory.getSession().exec("deleteFromPerson");

我们看到,通过这种方式,我们只需要将要用到的 sql 语句,整理并放到配置文件中就行了。 而执行的过程和封装的过程,交给 mybatis 帮助我们完成。这样,就有下面优点:

轻量级

灵活

方便

当然,在实际的过程中,会有很多 sql 语句,也会有其他复杂配置。 为了不至于配置文件过大而导致维护麻烦,mybatis 将配置文件分为了两类:

一个核心配置文件,里面配置数据库信息,还有指定其他的 mapper 文件。

多个 mapper 文件。虽然所有的 sql 放在一个 xml 中也没问题。但基于单一原则(还有其他原因),我们应该将操作不同表的语句分开来放在不同文件里。这样便于维护使用。

上面只是示例伪代码,下面才是 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>

  <!-- 我们可以配置多个数据库,这样方便我们在不同环境下的开发 -->

  <!-- 配置好了,后面就不需要改来改去了。切换数据库的时候,只需要将 default 指向我们相应的配置就可以了 -->

  <environments default="开发环境">

    <!-- 配置我们在生产环境中使用的数据库 -->

    <environment id="生产环境">

      <!-- 配置使用 jdbc 内建的事务处理 -->

      <transactionManager type="JDBC"/>

      <!-- 设置数据源 -->

      <dataSource type="POOLED">

        <property name="driver" value="${driver}"/>

        <property name="url" value="${url}"/>

        <property name="username" value="${username}"/>

        <property name="password" value="${password}"/>

      </dataSource>

    </environment>

    <environment id="开发环境">

      <transactionManager type="JDBC"/>

      <dataSource type="POOLED">

        <property name="driver" value="${driver}"/>

        <property name="url" value="${url}"/>

        <property name="username" value="${username}"/>

        <property name="password" value="${password}"/>

      </dataSource>

    </environment>

  </environments>

  <!-- 指定各个 mapper 文件 -->

  <mappers>

    <mapper resource="t/mapper/PersonMapper.xml"/>

  </mappers>

</configuration>

然后是 PersonMapper.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="t.dao.PersonDao">

  <!-- 这里,比我们上面的示例复杂了一点点。因为想要 mybatis 帮助我们更好做事,就要告诉她更多信息。 -->

  <!-- 首先,根据操作的不同,划分了不同的节点,比如 select 表示查询语句,delete 表示删除语句 -->

  <!-- 再次,关于 sql 名字,因为上面指定了 namespace,所以,我们下面这条 sql 的全名就是 t.dao.UserDao.selectFromPerson -->

  <!-- resultType: 告诉 mybatis,将查询出来的数据封装成 Person 类型 -->

  <!-- parameterType: 给我们的 sql 语句传一个参数进来 -->

  <select id="selectFromPerson" parameterType="java.lang.Integer" resultType="t.model.Person">

    SELECT id, name FROM person WHERE id=#{id}

  </select>

  <delete id="deleteFromPerson" parameterType="java.lang.Integer">

    delete from person where id=#{id}

  </delete>

</mapper>

配置文件有了,下面就是调用的方式:

// 1 ... 读取核心配置文件,创建 sessionfactory

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");

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

// 2 ... 获取 session

SqlSession session = sqlSessionFactory.openSession();

// 3 ... 进行查询,配置文件里,查询的 sql 语句我们给的名字是 t.dao.PersonDao.selectFromPerson,所以,查询如下

Person person = (Person) session.selectOne("t.dao.PersonDao.selectFromPerson", 1);

// 4 ... 进行删除,相应 sql 语句为 t.dao.PersonDao.deleteFromPerson

session.delete("t.dao.PersonDao.deleteFromPerson", 2);

// 5 ... 释放资源

session.close();

这样就 OK 了,这基本是 Mybatis 的全部了(虽然还有很多细节没说)。

当然,你有没有觉得,下面这样的语句写多了也会抓狂。

Person person = (Person) session.selectOne("t.dao.PersonDao.selectFromPerson", 1);

因为,sql 的名字写短了容易起冲突,写长了不好记,容易写错,并且因为是字符串 IDE 也不好给出相应提醒。另外,需要自己动手转型,也麻烦。

怎么办?再封装一丢丢就好了。

既然我们给这条 sql 起的名字是 t.dao.PersonDao.selectFromPerson,那么,我们就按照这个写法,创建相应的接口呗。

也就是:复辟我们的 dao 层!

package t.dao;

public interface PersonDao {

    Person selectFromPerson(int id);

    void deleteFromPerson(int id);

}

这样,整个调用的代码就变成了:

// 1 ... 读取核心配置文件,创建 sessionfactory

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");

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

// 2 ... 获取 session

SqlSession session = sqlSessionFactory.openSession();

// 3 ... mybatis 根据 PersonDao 接口生成相应的代理对象(代理模式,还记得吗)。

PersonDao personDao = session.getMapper(PersonDao.class);

// 4 ... 我们调用这个代理对象的相应方法

// mybatis 会通过反射的方式,查询到这个 PersonDao 的全类名为 t.dao.PersonDao,我们调用的方法名为 selectFromPerson,所以她也就明白了:

//  -- 我们是想要执行 t.dao.PersonDao.selectFromPerson 这条 sql 语句

//  -- 返回的数据应该封装成 Person

// 所以 mybatis 就会去找名字为 t.dao.PersonDao.selectFromPerson 的 sql 语句,查询到,封装结果到 Person 里。

Person person = personDao.selectFromPerson(1);

// 5 ... 释放资源

session.close();

有没有发现,这样封装了一下,好像事情变得更面向对象了,操作起来更爽手了。

也就这么简单。

我们的 MyBatis 教程。就这样结束了。

MyBatis + Spring

第一步,创建 Gradle 项目,引入相应 jar 包

compile (

        // Databases

        "cn.easyproject:ojdbc7:12.1.0.2.0",

        "c3p0:c3p0:0.9.1.2",

        // Spring

        "org.springframework:spring-web:$springVersion",

        "org.springframework:spring-aop:$springVersion",

        "org.springframework:spring-orm:$springVersion",

        // MyBatis And Plugin

        "org.mybatis:mybatis:3.4.2",

        "org.mybatis:mybatis-spring:1.3.1"

        )

配置文件

我们可以将 mybatis 的核心配置文件合并到 spring 中:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xmlns:context="http://www.springframework.org/schema/context"

      xmlns:tx="http://www.springframework.org/schema/tx"

      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

  <!-- 外部资源文件 -->

  <context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"/>

  <!-- 配置数据源 -->

  <!-- 这里使用的是 c3p0,比较经典的商业上比较成熟的数据源管理包 -->

  <!-- Spring 也内置了一个数据源管理类,我们开发环境可以用,但生产环境就不要用了。因为它太简陋了。 -->

  <!-- 现在使用比较多的,还有阿里云的数据源管理 jar 包,你们可以自行查询 -->

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

    <property name="user" value="${user}"/>

    <property name="password" value="${password}"/>

    <property name="jdbcUrl" value="${url}"/>

    <property name="driverClass" value="${driver}"/>

  </bean>

  <!-- 将 Sqlsessionfactory 的生成工作交给 spring -->

  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

    <!-- 指明使用的数据源 -->

    <property name="dataSource" ref="dataSource"/>

    <!-- 指明所有的 mapper 文件。虽然,也可以通过注解配置 mybatis,但在实际过程中,尽量用 xml 少用注解,因为这样便于维护。切记。-->

    <property name="mapperLocations" value="classpath:mybatis/*.xml"/>

    <!-- 指明默认的实体类的包名,如果指定了,我们在 mapper 里的 resultType="t.model.User" 可以简写为 resultType="User" -->

    <property name="typeAliasesPackage" value="classpath:t.model"/>

  </bean>

  <!--

      如果仅配置了 sqlSessionFactory,在代码中,调用 selectFromPerson,我们需要这样写:

      // 先通过容器获取 sessionfactory

      @Resource SqlSessionFactory sqlSessionFacotory;

      // 得到 mapper

      PersonDao personDao = sqlSessionFacotory.getMapper(PersonDao.class);

      // 执行方法

      personDao.selectFromPerson(1);

      但是,如果能将代码简化成这种形式,岂不是更美?

      // 直接从容器里取出初始化好的 personDao

      @Resource PersonDao personDao;

      // 指定方法

      personDao.selectFromPerson(1);

      想这样吗?可以,只需要在 Spring 中配置一个后置处理器就好了。

      简而言之,让 spring 在初始化好后,将 basePackage 里的所有接口都通过 mybatis 产生相应的代理类,然后放到容器里。

      这样,我们以后用的时候,只需要到容器里去拿就可以了。

  -->

  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

    <property name="basePackage" value="it.dao"/>

    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>

  </bean>

  <!-- 下面是声明式事务,跟之前差不多的 -->

  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"/>

  </bean>

  <tx:annotation-driven proxy-target-class="true"/>

</beans>

使用,比如,在 PersonService 类里面

@Service

@Transactional

public class PersonService {

    @Resource

    private PersonDao personDao;

    public getPersonById(int id) {

        return personDao.selectFromPerson(id);

    }

}

上一篇下一篇

猜你喜欢

热点阅读