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);
}
}
网友评论