主要参考
MyBatis中文网https://mybatis.net.cn/index.html
主要是略写一些自己的理解,并加以实例,还是文档全。
目录
核心1: SqlMapConfig.xml (xml映射器)
MyBatis的概念优点特性
1.半自动化ORM框架(对象关系映射)
Hibernate完整的ORM框架:
完整的ORM框架:对java对象的属性进行改变(obj.setID(1000)),数据库中的表数据也改变
2.自定义SQL(动态SQL/多表连接/子查询/存储过程)
3.封装大量的JDBC代码(只需要用Mybatis约定俗成的方式),简化DAO层代码
4.表间关系映射(一对一,一对多)
5.缓存应用(一级缓存和二级缓存),Hibernate有三级缓存
6.事务控制
7.XML配置和注解配置(@)
8.OGNL表达式(#{})
MyBatis运行流程
解释一下:
核心1: SqlMapConfig.xml (xml映射器)
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
通俗的来讲就是实现dao层,也就是实现对数据库操作的一些方法。(通常在Maven项目的Resources.mapper下定义,看个人习惯吧)
核心2:Config.xml (核心配置文件)
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
其中包括涉及配置一些之前所说的xml映射器啊,一些连接数据库的操作啊(配置数据源),一些环境信息啊,一些命名方式啊等等,这是功能最多的一个配置文件,至关重要。(通常在Maven项目的Resources下定义,看个人习惯吧)
核心3:SqlSession实例
SqlSessionFactoryBuilder->根据核心配置文件实现sqlSessionFactory初始化->SqlSession
然后借由SqlSession的一些方法实现对之前xml映射器中的一些方法进行调用。
所以我的理解Mybatis就是分3步,对应上面的3个核心。
xml映射器(xml mapper)
首先先要了解命名空间。
在之前版本的 MyBatis 中,命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。
命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。
命名解析:为了减少输入量,MyBatis 对所有具有名称的配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。
- 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
- 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。
简而言之命名空间是必须的,并且它的作用可以唯一标识sql语句,从而不用混用不同的sql方法(有点类似于C++ namespace的设计)
这里的命名空间在xml映射器中可以通过<mapper>标签实现声明,例如:
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
介绍SQL 映射文件只有很少的几个顶级元素:(看Mybatis文档)
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
这里只取我认为重要的介绍:
IMT1:关于各顶级元素中的属性的强调:
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。也就是统称的方法名 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
这也就是为什么Mybatis会符合ORM框架的原因,你可以直接把java bean的全限定名或别名作为参数传进去 或 返回。我下面举例子的Doc类就是个java bean。
IMT2: 参数符号#{}
告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中.也就是通常意义上的待定值。同样是上面的例子:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
IMT3: 数据库名称对应问题
sql语句中的变量名一定要对应自己的数据库中元组的属性名。
不使用xml映射器:
如果讨厌xml映射器嫌麻烦,你也可以使用Mybatis的jar包中给的一些注解来实现接口类,从而完成xml的功能。譬如:
public interface DocMapper {
@Select("select name from doc where id=#{did}")
String getDocNameById (int id);
}
也就是说你既可以只使用xml映射器,也可以只实现接口类,也可以两个都实现但要实现接口类全限定名 与xml映射器中的命名空间对应
核心配置文件 (Mybatis_config.xml)
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
通常的配置文件是这样的:
<?xml version="1.0" encoding="UTF-8"?><!-- xml的编码 -->
<!-- mybatis的config文件定义格式 -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--1.配置数据源的属性集文件-->
<!--2.常见的设置,比如开启驼峰命名规则-->
<!--3.配置类型别名-->
<!--配置单个类型别名-->
<!--扫描所有的-->
<!--4.配置JDBC连接-->
<!--5.加载dao层mapper(映射)文件-->
</configuration>
1.配置数据源的属性集文件
这里用到 属性(properties/property)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
当然也可以指定properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性,比如:
<properties resource="jdbc.properties"/>
jdbc.properties
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/doc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
mysql.username=root
mysql.password=Zz457178918
设置好的属性可以在整个配置文件中用来替换需要动态配置${}的属性值。比如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
2.常见的设置,比如开启驼峰命名规则
这里用到设置(settings)这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 这里以驼峰命名举例:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.配置类型别名
这里用到 类型别名(typeAliases),类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!--配置单个类型别名-->
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
<!--扫描所有的-->
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
4.配置JDBC连接(这里还是以文档为主看)
这里用到环境配置(environments-environment),MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。简而言之就是用来连接数据库,声明数据库用户名,地址,密码啥的。
■environments元素
◆表示配置MyBatis的多套运行环境 ,将SQL映射到多个不同的数据库上
◆子元素节点: environment ,但是必须指定其中一个默认运行环境
(通过default指定)
每个Sq|SessionFactory实例只能选择一个运行环境。举个例子
transactionManager(事务管理器)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
- 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
这里的pool的意思是连接池的意思,在使用mybatis时最费时间的是连接操作,也就是通常意义所说的connection,连接池的意思就是每次用完connection,不扔,把他放到一个list里面以防下次用,这样就能快启动了。举例子:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
5.加载dao层mapper(映射)文件
这里用到映射器(mappers),既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
SqlSession实例 (截取文档部分)
首先前置知识:
简而言之目的就是为了得到SqlSession在用SqlSession的方法去调用 xml mapper中的方法从而实现对数据库的访问使用。
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
注意(session.selectOne的第一个参数是 对应的命名空间+id对应的标识方法,后面跟传入参数)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
Doc doc = (Doc) session.selectOne("lmx.dao.DocMapper.getDocById", 1);
System.out.println(doc);
session.close();
}
诚然,这种方式能够正常工作,对使用旧版本 MyBatis 的用户来说也比较熟悉。但现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 DocMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
例如:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
DocMapper mapper = session.getMapper(DocMapper.class);
Doc doc = (Doc) mapper.getDocById( 1);
System.out.println(doc);
session.close();
}
第二种方法使用了反射机制,值得注意的是之前我说了dao层只需要一个 xml映射器 就可以了,第一种方法就能跑。但是如果要用第二种方式的话,就必须实现对应的dao层接口类,这里的接口类就是所谓的DocMapper,并且你之前在xml映射器中设置的命名空间namespace要对应DocMapper接口类的全限定名,接口类中的方法要对应相应的xml映射器中的id名,方法的返回类型,参数个数也要对应。
public interface DocMapper {
Doc getDocById (int id);
}
完整实例,一步一步做
项目工程
jdbc.properties
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/doc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
mysql.username=root
mysql.password=Zz457178918
JavaBean (Doc.java)
package lmx.entity;
public class Doc {
int id;
String name;
public Doc(int id, String name) {
this.id = id;
this.name = name;
}
public Doc() {
}
@Override
public String toString() {
return "Doc{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
xml映射器 (Docmapper.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="lmx.dao.DocMapper">
<select id="getDocById" resultType="Doc">
select * from doc where id = #{docId}
</select>
</mapper>
核心配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><!-- xml的编码 -->
<!-- mybatis的config文件定义格式 -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--properties-->
<!-- 配置数据库来源文件 -->
<properties resource="jdbc.properties"/>
<!-- 开启驼峰命名则 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 配置对应的实体类 -->
<typeAliases>
<package name="lmx.entity"/>
</typeAliases>
<!-- 配置JDBC连接 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<!-- mapper文件所在位置 -->
<!-- 映射mapper文件 -->
<mappers>
<mapper resource="mapper/DocMapper.xml"></mapper>
</mappers>
</configuration>
绑定接口(Docmapper.java)
package lmx.dao;
import lmx.entity.Doc;
public interface DocMapper {
Doc getDocById (int id);
}
sqlsession实例化并调用方法
import lmx.dao.DocMapper;
import lmx.entity.Doc;
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 Test {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
DocMapper mapper = session.getMapper(DocMapper.class);
Doc doc = (Doc) mapper.getDocById( 1);
System.out.println(doc);
session.close();
}
}
}