Java Web之 MyBatis框架
1. MyBatis的概念:
MyBatis是支持定制化、SQL、存储过程以及高级映射的优秀的持久层的框架。MyBatis避免了几乎所有的JDBC代码和手工设置参数以及抽取结果集。MyBatis使用简单的XML或注解来配置和映射基本体,将接口和Java的POJO(plain old java Objects,普通的java对象)映射成数据库中的记录。
MyBatis让程序主要精力放在sql上,通过MyBatis提供的映射方式,自由灵活生成满足需要的sql语句,MyBatis可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。
2. MyBatis的执行流程:
MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一个是配置文件,一个是java代码的注解,将sql的配置信息加载成一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。获取一个SqlSession,SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。
SQL解析:当api接口层接收到调用请求时,会接收到传入sql的id和传入对象(可以是map或者基本数据类型),MyBatis会根据sql的id找到相应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析以后可以得到最终要执行的sql语句和参数。
SQL执行:将最终得到SQL和参数拿到数据可进行执行,得到操作数据库的结果。
结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
传统的Dao层开发:(Hibernate框架开发):
- 需要程序员编写Dao接口和实现类。
- 需要在Dao实现类中注入一个SqlSessionFactory工厂。
- 需要在Dao实现类中写大量SQL语句。
使用MyBatis框架开发:
- 只需要程序员编写Dao接口(也称mapper接口),不需要编写实现类
- 也不需要在实现类中写SQL语句
- 在Dao接口对应的映射文件中(XXXMapper.xml)写SQL语句,对应接口文件中方法名。
3. MyBatis的执行过程:
1. 配置mybatis的配置文件,SqlMpConfig.xml(名称不固定)。
2. 通过配置文件,加载mybatis运行环境,创建SqlSessionFactory(单例模式)工厂。
3. 通过SqlSessionFactory创建SqlSession(SqlSession是一个面向用户接口(提供数据库操作方法),实现对象是线程不安 全的,建议SqlSession应用场合在方法体内。
4. 调用SqlSession的方法去操作数据。(如果需要提交事务,需要执行SqlSession的commit()方法)。
5. 释放资源,关闭SqlSession。
4. MyBatis的使用流程:
1. 先导入mybatis-3.x.jar和数据库驱动jar包。
2. 在数据库访问层中添加如下文件:
一个核心配置文件(例如:SqlMapConfig.xml)
多个接口映射文件:XXXMapper.xml
3.编写代码:解析核心配置文件,创建SqlSessionFactory工厂。
4.通过SqlSessionFactory工厂创建一个SqlSession实例,进一步得到映射接口的实例。
5.在业务逻辑层中调用映射器接口中的方法进行业务处理。
6. 关闭SqlSession对象。
5. MyBatis的核心配置文件SqlMapConfig.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>
<!-- 属性:定义配置外在化 -->
<properties></properties>
<!-- 设置:定义mybatis的一些全局性设置 -->
<settings>
<!-- 具体的参数名和参数值 -->
<setting name="" value=""/>
</settings>
<!-- 类型名称:为一些类定义别名 -->
<typeAliases></typeAliases>
<!-- 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系 -->
<typeHandlers></typeHandlers>
<!-- 对象工厂 -->
<objectFactory type=""></objectFactory>
<!-- 插件:mybatis的插件,插件可以修改mybatis的内部运行规则 -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- 环境:配置mybatis的环境 -->
<environments default="">
<!-- 环境变量:可以配置多个环境变量,比如使用多数据源时,就需要配置多个环境变量 -->
<environment id="">
<!-- 事务管理器 -->
<transactionManager type=""></transactionManager>
<!-- 数据源 -->
<dataSource type=""></dataSource>
</environment>
</environments>
<!-- 数据库厂商标识 -->
<databaseIdProvider type=""></databaseIdProvider>
<!-- 映射器:指定映射文件或者映射类 -->
<mappers></mappers>
</configuration>
Properties:
properties元素主要是用来定义配置外在化,比如数据库的连接属性等。这些属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,亦可以通过properties元素的子元素来传递。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如在数据源中使用的例子:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
Settings:
setting是指定MyBatis的一些全局配置属性,这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为,所以我们需要清楚的知道这些属性的作用及默认值。
一个完整的settings元素示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
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的地方。
插件(plugins):
MyBatis允许你在已映射的语句执行过程中的某一点进行拦截调用。默认情况下,Mybatis允许使用插件来拦截的方法调用包括:
Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler(getParameterObejct,setParameters)
ResultSetHandler(handlerResultSets,handlerOutputParameters)
StatementHandler(prepare,parameterize,batch,update,query)
配置环境(environments):
MyBatis可以配置成适应多种环境,这种机制有助于将sql映射应用于多种数据库中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同的Schema的多个生产数据库,想使用相同的sql映射。许多类似的用例。
尽管可以配置多个环境,但是每个SqlSessionFactory实例只能选择其一。
所以,如果想连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
注意这里的关键点:
默认环境的ID(比如:default="development")
每个environment元素定义的环境ID(比如:id="development")
事务管理器的配置(比如:type="JDBC")
数据源的配置(比如:type="POOLED")
映射器(mappers):
既然MyBatis的行为已经由上述元素配置完了,现在就要定义SQL映射语句了。但是首先需要告诉MyBatis到哪里去找到这些语句。Java在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉MyBatis到哪里去找映射文件。可以使用相对于类路径的资源引用、或完全限定资源定位符(包括file:///的URL),或类名和包名等等。例如:
<!-- Using classpath relative resources -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
6. MyBatis的Mapper映射文件:
Mapper映射文件就是所有sql语句放置的地方
<?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" >
<!-- the code template created by wxmp-generator @author hermit @version 2.0 @date 2018-04-17 10:43:21 -->
<mapper namespace="com.wxmp.wxcms.mapper.MsgArticleDao">
<resultMap type="com.wxmp.wxcms.domain.MsgArticle" id="msgArticleMap">
<id property="arId" column="ar_id"/>
<result property="title" column="title" />
<result property="author" column="author" />
<result property="content" column="content" />
<result property="digest" column="digest" />
<result property="showCoverPic" column="show_cover_pic"/>
<result property="picUrl" column="pic_url" />
<result property="url" column="url" />
<result property="thumbMediaId" column="thumb_media_id"/>
<result property="contentSourceUrl" column="content_source_url" />
<result property="mediaId" column="media_id" />
<result property="newsId" column="news_id" />
<result property="newsIndex" column="news_index" />
</resultMap>
<select id="getByNewsId" parameterType="int" resultType="com.wxmp.wxcms.domain.MsgArticle">
SELECT * FROM wxcms_article WHERE NEWS_ID = #{newsId}
</select>
<select id="getById" parameterType="int" resultType="com.wxmp.wxcms.domain.MsgArticle">
SELECT * FROM wxcms_article WHERE ID = #{id}
</select>
<insert id="add" parameterType="com.wxmp.wxcms.domain.MsgArticle" flushCache="true" useGeneratedKeys="true" keyProperty="arId">
INSERT INTO wxcms_article (
TITLE,
AUTHOR,
CONTENT,
DIGEST,
SHOW_COVER_PIC,
URL,
PIC_URL,
THUMB_MEDIA_ID,
MEDIA_ID,
CONTENT_SOURCE_URL,
NEWS_ID,
NEWS_INDEX)
VALUES (
#{title},
#{author},
#{content},
#{digest},
#{showCoverPic},
#{url},
#{picUrl},
#{thumbMediaId},
#{mediaId},
#{contentSourceUrl},
#{newsId},
#{newsIndex})
</insert>
<insert id="insertByBatch" parameterType="java.util.List">
insert into wxcms_article
(TITLE,
AUTHOR,
CONTENT,
DIGEST,
SHOW_COVER_PIC,
URL,
PIC_URL,
THUMB_MEDIA_ID,
MEDIA_ID,
CONTENT_SOURCE_URL,
NEWS_ID,
NEWS_INDEX)
values
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.title},
#{item.author},
#{item.content},
#{item.digest},
#{item.showCoverPic},
#{item.url},
#{item.picUrl},
#{item.thumbMediaId},
#{item.mediaId},
#{item.contentSourceUrl},
#{item.newsId},
#{item.newsIndex})
</foreach>
</insert>
<update id="update" parameterType="com.wxmp.wxcms.domain.MsgArticle" flushCache="true">
UPDATE wxcms_article SET
<if test="title != '' and title != null">
TITLE=#{title},
</if>
<if test="author != '' and author != null">
AUTHOR=#{author},
</if>
<if test="content != '' and content != null">
CONTENT=#{content},
</if>
<if test="showCoverPic != '' and showCoverPic != null">
SHOW_COVER_PIC=#{showCoverPic},
</if>
<if test="thumbMediaId != '' and thumbMediaId != null">
THUMB_MEDIA_ID=#{thumbMediaId},
</if>
PIC_URL=#{picUrl},
DIGEST=#{digest},
CONTENT_SOURCE_URL=#{contentSourceUrl}
WHERE ar_id = #{arId}
</update>
<delete id="delete" parameterType="int" >
DELETE FROM wxcms_article WHERE ID = #{id}
</delete>
<delete id="deleteByBatch" parameterType="int" >
DELETE FROM wxcms_article WHERE NEWS_ID = #{id}
</delete>
</mapper>
SQL 映射XML 文件一些初级的元素:
1. cache – 配置给定模式的缓存
2. cache-ref – 从别的模式中引用一个缓存
3. resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集中加载对象
4. sql – 一个可以被其他语句复用的SQL 块
5. insert – 映射INSERT 语句
6. update – 映射UPDATE 语句
7. delete – 映射DELEETE 语句
8. select - 映射SELECT语句
(1)resultMap:
引用外部的resultMap名,结果集映射是MyBatis中最强大的特性,许多复杂的映射都可以轻松解决。
可以通过resultMap完成一些高级映射:
将关联查询的列映射到一个pojo属性中(一对一)
将关联查询的列映射到一个List<pojo>中(一对多)
type、id:
type为java实体类,id为此resultMap的标识
(2)id、result:
id,result是最简单的映射,id为主键映射,result是数据库表字段到实体类属性的映射。
id,result语句属性配置细节:
property: 需要映射到JavaBean的属性名称
column: 数据表的列明或者标签别名
(3)Mapper:
(4)insert:
Id,parameterType,resultType
id:标识mapper中的方法
parameterType:指定输入参数的类型
resultType:指定mapper.java中方法返回值的类型
(4)select:
Id,parameterType,resultType
id:标识mapper中的方法
parameterType:指定输入参数的类型
resultType:指定返回参数的类型
(5)delete:
Id,parameterType,resultType
id:标识mapper中的方法
parameterType:指定输入参数的类型
resultType:指定返回参数的类型
(6)update:
Id,parameterType,resultType
id:标识mapper中的方法
parameterType:指定输入参数的类型
resultType:指定返回参数的类型
(7)#{id}
#{}:标识一个占位符
id: 标识接收输入的参数,参数名称就是id
${}:标识拼接sql串,将接收到参数的内容不加任何修饰拼接到sql中
#{}:中指定pojo的属性名,接收到pojo对象的属性值,MyBatis通过OGNL获取对象的属性值。
7. MyBatis的优缺点:
优点:
- 易于上手和掌握。
- sql写在xml里,便于统一管理和优化。
- 解除sql与程序代码的耦合。
- 提供映射标签,支持对象与数据库的orm字段关系映射。
- 提供对象关系映射标签,支持对象关系组件维护。
- 提供xml标签,支持编写动态sql。
缺点:
- sql工作量很大,尤其是字段多,关联表多。
- sql依赖数据库,导致数据库移植性差。
- 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
- DAO层过于简单,对象的组装量较大。
- 缓存使用不当,容易产生脏数据。
8. myBatis和hibernate的区别:
1、hibernate 是一个标准的ORM框架(对象关系映射框架),不需要程序写sql,sql语句自动生成,对sql语句进行优化、修改 比较困难。全自动的、完全面向对象的持久层框架
2、mybatis专注于sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。是一个不完全的ORM框架,虽然程序员自 己写sql,mybatis也可以实现映射(输入映射,输出映射)半自动化的持久层框架。
3、Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
4、Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
5、Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
6、Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
7、MyBatis可以进行更为细致的SQL优化,可以减少查询字段。