一、Mybatis框架简单介绍和环境构建
众所周知,在开发大多数系统的后台时,都能粗略地把任务分为表现交互层,业务服务层,数据持久层,Mybatis就是这样一个位于数据持久层的框架。经过一天的资料查阅,我准备将对它的认识记录在该博客中。
Mybatis框架的作用有:
- 能管理数据库连接池,配置完成后就无需再关心数据库连接即可直接和数据库进行会话。
- 拥有 灵活的 ORM功能,可以自定义ORM过程中大多数中间类。(比如类型映射,对象工厂等)
- 通过配置mapper文件可以手写 动态的 sql语句将结果集映射到对象中。
- 通过第二点,能很方便的写一个DAO类。
- 支持存储过程和事务。具有一级、二级缓存功能。
- 能很方便的和Spring等框架进行集成。
Mybatis就是这样的一个框架。在它的官网,能获取到一些较为简单的介绍:http://www.mybatis.org/spring/zh/index.html
由于我是通过Maven项目使用的该框架,所以使用之前需要编写pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.data</groupId>
<artifactId>datatest1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- 依赖关系 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- junit.jar -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
</dependencies>
</project>
pom.xml文件中,最主要的依赖便是数据库驱动和mybatis了。定义好pom后,再编写mybatis本身所需要的配置文件,就可以快速开始了。
二、Mybatis的主要基础组件及生命周期
在写MyBatis的配置文件之前,我先理一理MyBatis中有哪些重要的组件,他们的生命周期是什么样的,以及为什么需要这个配置文件。相信看了下面,心中应该会有较为明确的答案。
MyBatis最重要的基础组件有:
SqlSessionFactoryBuilder: 结构化查询语言会话工厂构造器,该构造器将会读入Mybatis的xml配置文件或者设置好的Configuration对象来获得一个结构化查询语言会话工厂(SqlSessionFactory)。所以,通常在获取到工厂后它就没有其它作用了,也就是可以被销毁了。
SqlSessionFactory: 结构化查询语言会话工厂,该工厂用于获取结构化查询语言会话(SqlSession),也可以获取之前构造该工厂时的配置对象。由于该对象管理着数据库连接池,所以在一般情况下,对于同一个数据库实例,我们应该只创建一个静态最终的该对象。
SqlSession: 结构化查询语言会话,该对象用于和数据库的交互,即可以发送SQL也并返回结果,也可以获取映射接口的对象。相当于JDBC中的Connection对象。通过获取映射接口的对象可以通过方法调用来实现SQL的发送和返回。由于它只存在于数据库事务过程中,所以在事务执行完成后不需要使用它时,需要通过finally语句及时地保证正确地关闭它,否则将造成资源泄漏。
Mapper: 映射器,它是由一个Java接口和XML文件构成的对象(也可以由接口和注解组成),需要给出对应的SQL和映射规则,由SqlSession.getMapper(xxx.class)来得到它。它也可以负责发送SQL去执行并返回结果。和SqlSession是同级别的东西,只是用起来更加简单,可读。所以,它应该和它的会话保持同样的生命周期。以保证程序的正确运行与健壮。
三、MyBatis的环境配置以及映射配置
在环境配置文件中,我们可以设置数据库连接池的配置,事务管理器的配置,还必须配置数据库驱动,连接地址以及账号和密码。以上这些东西组成一个 “环境” ,而MyBatis的环境配置文件可以配置多个环境。以方便在代码中获取该环境的会话工厂。如果需要使用该框架,最后还需要将映射配置到mappers标签中。
比较全面的环境配置示例和注释如下:
myBatisConfig.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>
<!-- 通过该标签可以为配置增加属性,以便配置文件的上下文中以"${name}"的形式引用它
也可以通过properties的 resource 属性引用属性文件
<properties [resource="xxxx.properties"]>
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://xxxxxxx />
....
</properties>
-->
<!--设置可以配置MyBatis的缓存,延迟加载,映射相关,日志打印等等等高级配置.
<settings>
<setting name="xxxxx" value="xxxx">
.....
</settings>
-->
<!-- 通过该标签可以为类型给一个别名,或者引用一个包名中所有有注解的别名类型 ,以便MyBatis上下文中的引用
MyBatis为Java的所有基本类型以及包装类型和部分容器,日期,大数定义好了别名。源码在TypeAliasRegistry类中
一般用在映射文件中
<typeAliases>
<typeAlias alias = "别名" type="类型" />
<package name="包名"/>
</typeAliases>
-->
<!--用于将JDBC类型(jdbcType)和Java类型(javaType)相互转换,MyBatis内置了大部分类型,源码在:TypeHandlerRegistry类中
可以通过实现typeHandler接口,或者继承BaseTypeHandler类自定义自己的类型处理器
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="String" handler="com.xxx.xx.xxxHandler">
.....
</typeHandlers>
-->
<!--MyBatis在构建一个结果集返回时,都会使用对象工厂去构建POJO.默认的对象工厂在 DefaultObjectFactory 类中
可以在这类定制自己的对象工厂,前提是实现ObjectFacotry接口
<objectFactory type="com.xxxx.xxx.MyObjectFactory">
<property name="name" value="MyObjectFactory" />
</objectFactory>
-->
<!--插件可以改变一些MyBatis的核心行为,比如可以拦截部分数据
<plugins>
</plugins>
-->
<!-- 注册默认环境配置为development -->
<environments default="development">
<!--一个id为 development 的数据库配置环境 , 不同的SqlSessionFactory可以选择不同的环境-->
<environment id="development">
<!-- 采用JDBC事务管理 type="[JDBC|MANAGED|自定义]"-->
<transactionManager type="JDBC"/>
<!--指定数据库连接池的属性 type="[UNPOOLED|POOLED|JNDI|自定义]"-->
<dataSource type="POOLED">
<!-- 1.加载数据库驱动:-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- 2.数据库连接地址: -->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/javatest?
characterEncoding=utf8&serverTimezone=GMT&useSSL=FALSE"/>
<!-- 数据库用户... -->
<property name="username" value="xxxxxxxxxxxxx"/>
<!-- 数据库密码... -->
<property name="password" value="xxxxxxxxxxxxxx"/>
</dataSource>
</environment>
</environments>
<!--数据库厂商标识,若MyBatis有多个不同厂商的数据库管理系统环境,则可以在这为数据库提供一个标识
以下是默认的配置,不用自己编写
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver" />
<property name="MySQL Server" value="mysql" />
<property name="DB2 Server" value="db2" />
<property name="Oracle Server" value="oracle" />
</databaseIdProvider>
-->
<!-- 注册映射器 -->
<mappers>
<!-- 通过相对路径引用-->
<mapper resource="com/xxx/xxxxxxx/UserMapper.xml"></mapper>
<!-- 通过绝对路径引用-->
<mapper resource="file:var/mappers/com/xxxxx/xxxxx/UserMapper.xml"></mapper>
<!-- 通过类注册引用 这里需要注意的是,.xml映射文件和接口必须位于同一目录,且文件名相同-->
<mapper class="com.xxxx.xxxxxx.UserMapper"></mapper>
<!-- 通过包名引入映射器 条件和上面一样-->
<package name="com.xxx.xxxxx.xxxx" />
</mappers>
</configuration>
需要注意的是,因为这里提供了多个设置属性的方法,所以它们是有优先级次序的:
通过方法参数传递的属性 > resource/url属性中指定的配置文件 > properties属性中指定的属性
在之前的文件里注册了一个映射文件,它的作用是定义数据库表和Java对象之间的映射,以及动静态SQL模版,结果集的定义等。由于映射文件的定义比较灵活,一个映射文件也不好演示所有的功能,所以下面的示例文件只演示了一些基本功能:
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="org.mapper.UserMapper">
<!-- 自定义返回结果集 -->
<resultMap id="userMap" type="org.data.User">
<id property="id" column="id" javaType="java.lang.Long"></id>
<result property="name" column="name" javaType="java.lang.String"></result>
<result property="contact" column="contact" javaType="java.lang.String"></result>
<result property="telephone" column="telephone" javaType="java.lang.String"></result>
<result property="email" column="email" javaType="java.lang.String"></result>
<result property="remark" column="remark" javaType="java.lang.String"></result>
<result property="photo" column="photo" javaType="java.lang.String"></result>
</resultMap>
<!--定义一个插入体,映射为Java接口中的insertUser方法
通过设置keyProperty和useGeneratedKeys属性可以实现主键回调。
在该例子中即是将User对象中的id设置为新插入行的主键值。通常用于插入记录后获取其自增id
-->
<insert id="insertUser" keyProperty="id" useGeneratedKeys="true" parameterType="org.data.User">
insert into customer values(#{name},#{contact},#{telephone},#{email},#{remark},#{photo})
</insert>
<!--定义一个删除体,映射为Java接口中的deleteUser方法 -->
<delete id="deleteUser">
delete from customer where id = #{id}
</delete>
<!--定义一个修改体,映射为Java接口中的updateUser方法-->
<update id="updateUser">
update customer set id = #{user.id},name = #{user.name},contact=#{user.contact}
,telephone=#{user.telephone},email=#{user.email},remark=#{user.remark},photo=#{user.photo}
where id=#{id}
</update>
<!--定义一个修改体,映射为Java接口中的updateUser2方法 -->
<update id="updateUser2">
update customer set id = #{id},name = #{name},contact=#{contact},telephone=#{telephone}
,email=#{email},remark=#{remark},photo=#{photo}
where id=#{id}
</update>
<!--定义一个查询体,映射为Java接口中的selectUserById方法,参数为一个int值,返回类型为org.data.User
其中 select * from customer where id = #{id} 是SQL模版, #{id} 相当于 JDBC 中的 ? 仅代表一个有可读性的占位符
将占位符写的和POJO类中的属性名保持一致可以实现自动映射
-->
<select id="selectUserById" parameterType="int" resultType="org.data.User">
select * from customer where id=#{id}
</select>
<!--定义一个查询体,映射为Java接口中的selecAllUser方法.返回到上面定义的结果集 userMap 中 -->
<select id="selectAllUser" resultMap="userMap">
select * from customer
</select>
</mapper>
Java映射接口为:
org.mapper.UserMapper
package org.mapper;
import org.apache.ibatis.annotations.Param;
import org.data.User;
import java.util.List;
public interface UserMapper {
/**
* 新增用户
* @param user
* @return
* @throws Exception
*/
public int insertUser(User user)throws Exception;
/**
* 修改用户
* @param user,id
* @return
* @throws Exception
*/
public int updateUser(@Param("user") User user,@Param("id") Long id)throws Exception;
/**
* 修改用户2
* @param user,id
* @return
* @throws Exception
*/
public int updateUser2(User user)throws Exception;
/**
* 删除用户
* @param id
* @return
* @throws Exception
*/
public int deleteUser(Long id)throws Exception;
/**
* 根据id查询用户
* @param id
* @return
* @throws Exception
*/
public int selectUserById(Long id)throws Exception;
/**
* 查询所有用户信息
* @return
* @throws Exception
*/
public List<User> selectAllUser()throws Exception;
}
以上的映射文件及接口则是MyBatis的核心功能了,但由于它的灵活,我没有将动态SQL写到这里,这里只介绍了一些基本的概念及用法。
在映射文件中,最重要的几个概念有:
parameterType:即参数类型,是我们传给Sql语句参数的类型,由于类型处理器的存在,所以SQL参数可以接受Java的基本类型以及引用类型,但需要定义他们之间的映射关系。由于MyBatis已经帮我们注册了Java的通用类型,并且还提供了默认的自动映射功能。所以大多数情况下我们并不需要填写该值。当然,在多参数的情况下是必须填写的。单参数情况下填写也有助于代码的可读性。
resultType:即结果类型,由于插入/更新/删除都是返回的操作成功的条目数,所以结果类型固定为int而不用改动,这里的结果类型参数是用于select标签中的属性,用于定义存储select语句获取的结果集。它也可以是内置的通用类型,也可以是我们定义的POJO,更强大的是还能存储在我们自定义的resultMap中。
resultMap:它是映射集的引用,可以执行强大的映射功能,我们在resultType和resultMap中选择一个作为查询结果的存储集,它能给我们一个自定义映射规则的机会。
在上面的例子中,演示了很多常用功能,比如主键回填,自定义并使用映射集,单参数和多参数的传参等等等。所以在这里我想介绍一下该映射文件的使用基础:
在编写以及使用映射文件的过程中,我们要牢记该文件的作用和职能,我们应该为它提供些什么?它又会为我们做哪些工作?只要弄清楚这些,我相信在编写映射时,你不再会为一些莫名其妙的错误感到疑惑。
映射文件 即是用于定义Java和SQL之间关系的文件,它需要一个Java接口用于接受和执行配置文件中的SQL语句,它需要将SQL语句需要的东西以Java的形式传入和返回。这是一个ORM映射文件最基本的东西。在高级功能中它还可以做到根据条件标签动态生成SQL语句,自定义复杂的映射集作为参数或返回集,以及原生SQLMS中有的功能(比如设置超时)和对它的优化(比如结果缓存)等等。
在这里只谈论最基本的功能,那便是将Java接口和SQL语句联系起来,以及Java类型和SQL类型的映射。
Java与SQL的联系
在上面的文件中,Java接口和映射xml组合起来表示一组映射关系,MyBatis通过配置文件中的mappers标签可以知道它们的存在。在实际使用中可以通过会话(SessionFactory产生的Session)获取该映射接口,然后通过接口的方法便可以调用SQL语句。
Java类型与SQL类型的映射
单参数映射
关于Java类型与SQL类型的关系映射,MyBatis主要通过类型注册来实现Java通用类型与SQL类型的映射关系(映射的定义),然后通过自动映射(映射的使用)就可以很方便的实现单参数的传参。自动映射要求表的列名和JavaBean的属性名称相同。如果数据库采用下划线方式命名,JavaBean采用驼峰式命名。则可以通过设置mapUnderscoreToCamelCase = true 来实现自动映射。
多参数映射1
对于多参数传参需要使用map<String,String>的形式并且设该标签属性parameterType=“map”,接口的参数也为对应的map<String,String>来实现。
多参数映射2
或者使用@Param(“xxx”)注解接口参数的形式来手动指定映射,然后再进行传参。
多参数映射3
对于参数比较复杂的情况,以上两种方式可能都不太好。这时我们可以将SQL所需要的参数封装为一个JavaBean,这样一来就将多参数映射转换为了单映射的情况。
结果参数的映射
select语句的返回结果通常都是一个集,所以一般情况下可以用resultType属性来指定容纳这个集的类型,该类型可以是Java基本类型或引用类型。由于是单参数,所以MyBatis会自动帮我们实现自动映射。如果要求更为复杂的映射关系可以像上面那样通过自定义resultMap来实现复杂的映射引用。
Tips:在IDEA中,如果将映射mapper.xml文件放在src目录里,则Mybatis的配置文件会找不到该xml文件。如果是Maven项目可以加上这句话来解决:
<build>
<!-- 包含java目录中的xml资源 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
四、小结
在本文中,提到了如下几点:
- MyBatis环境的构建 – Maven配置
- MyBatis中主要基础组件的生命周期
- MyBatis的配置文件书写及意义
- MyBatis的映射功能的简单描述
五、更加详细的配置文档
更为详细的配置文档:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeAliases