MyBatis 整理

MyBatis概述

MyBatis是主流的Java持久层框架,也是一种ORM框架。由于性能优异,高度灵活、可优化性、易于维护等特点被广泛使用。

原本是Apache的一个开源项目:iBatis,后来改名为MyBatis。提供包括SQL Maps和DAO(Data Access Objects),消除了JDBC代码和参数的手工设置以及结果集的检索。使用XML或者注解的方式进行POJO的数据库映射。不同与Hibernate全映射框架,MyBatis是一个半自动的映射,需要手动匹配POJO、SQL和映射关系。

文档

  1. MyBatis环境构建
  2. MyBatis的工作原理
  3. 与Spring进行整合

MyBatis环境

项目引入MyBatis jar包即可。

配置文件:

<?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 /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

MyBatis 配置项的顺序不能颠倒。如果颠倒了它们的顺序,那么在 MyBatis 启动阶段就会发生异常,导致程序无法运行。

MyBatis的工作原理

UrcYon.png

与Spring整合

引入相关的依赖,将MyBatis的SqlSessionFactory交由Spring进行构建。

<!--配置数据源-->
...
<!--配置MyBatis工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--MyBatis核心配置文件-->
    <property name="configLocation" value="classpath:com/mybatis/mybatis-config.xml"/>
</bean>

<!--基于MapperScannerConfigurer的数据操作接口整合-->
<!--将@Mapper注解自动装配为MyBatis的映射接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

  1. 创建PO

  2. 创建DAO

    @Mapper 注解的DAO接口

  3. 创建Mapper 对应的PO的XML

    DaoMapper.XML

MyBatis映射器

MyBatis的强大之处体现在SQL映射文件上,映射器是MyBatis最复杂且最重要的组件,由一个接口加上SQL映射文件组成。除了XML外也有注解。

SQL映射文件(.xml)主要使用的配置元素:

  1. select
  2. insert
  3. update
  4. delete
  5. sql
  6. resultMap

主要是查询、插入、更新、删除、数据库返回结构处理。

select

Urc0QU.png

.示例:

比如存在一个UserMapper.xml,存在DAO接口方法:selectAllUser

<select id="selectAllUser" resultType="com.po.User" parameterType="map或者一个普通JavaBean">
	select * from user
	where uname like concat('%',#{u_name},'%')
	and usex=#{u_sex}
</select>

选择map或者JavaBean来传递查询参数看情况而定。

insert

用于插入数据库返回一个整数,表示影响的行数。属性基本与查询相同,特有属性:

  1. keyProperty

    将插入或更新的返回值,赋予某个PO类的某个属性。常设置主键对应的属性。

  2. keyColumn

    用来设置第几列是主键,表里第一列不是主键时需要设置。

  3. useGeneratedkeys

    设置该属性为true时,将使用JDBC的getGeneratedKeys()获取数据库内部产生的主键,默认为false。

业务上需要在插入成功时,返回当前插入对象的主键,这时候我们就需要进行主键回填

自动递增的情况下:

如mysql:

将会回填到添加进入的user的主键uid

<insert id="addUser" parameterType="com.po.User" keyProperty="uid" useGeneratedKeys="true">
	insert into user(uname,usex) values(#{uname},#{usex})
</insert>

在主键不自动递增的情况下:

可以使用MyBatis的<selectKey>来指定生成主键。

<insert id="addUser" parameterType="com.po.User">
    <selectKey keyProperty="uid" resultType="Integer" order="BEFORE">
        select if(max(uid) is null , 1 , max(uid)+1) as newUid from user 
    </selectKey>
	insert into user(uid,uname,usex) values(#{uid},#{uname},#{usex})
</insert>

其中keyProperty指定了返回PO类的属性为uid,order指定可以选择 BEFORE或者AFTER,分别指定在插入数据前执行还是插入后执行。

update

<update id="updateUser" parameterType="com.po.User">
	update user set uname=#{uname},usex=#{usex} where uid=#{uid}
</update>

delete

<delete id="deleteUser" parameterType="com.po.User">
	delete from user where uid=#{uid}
</delete>

将上述的CRUD等元素在对应的Mapper.xml中进行对应方法接口的SQL映射是关键。

sql

<sql>主要是用来定义一个SQL语句的一部分,方便重用。

如:

<sql id="selectColumns">uid,uname,usex</sql>

<select id="selectAllUser" resultType="com.po.User">
	select <include refid="selectColumns"/> from user
</select>

使用<include refid="引用的sql语句">进行嵌入。

resultMap

该元素表示结果映射集,主要用以定义映射规则、级联的更新以及定义类型转化器等。

主要体现在自定义返回结果映射结构。

一个resultMap的定义

<resultMap type="需要的POJO类" id="该结果集结构的唯一标识">
	<constructor> <!--类在实例化时用以注入结果到构造方法,POJO未定义无参数构造方法时使用-->
    	<idArg/> <!--ID参数,对应ID-->
        <arg/> <!--普通结果-->
    </constructor>
    <id property="POJO对应主键属性" column="查询结果的列名"/> <!--表示哪一列是主键-->
    <result property="POJO对应属性" column="查询结果的列名"/> <!--表示POJO和数据表普通列的映射关系-->
    <association property=""/> <!--用以一对一关联-->
    <collection property=""/> <!--一对多,多对多关联-->
    <discriminator javaType=""> <!--使用结果值来决定那些结果进行映射-->
    	<cass value=""/> <!--基于某些值的结果映射-->
    </discriminator>
</resultMap>

对于查询的结果映射可以使用Map,也可以使用POJO,大多数使用POJO,可以进行自动映射。

当存在级联查询的时候,需要进行一个resultMap的定义使用。

示例一个使用POJO进行返回。

省略POJO类,等,关注于xml的编写

<!--
User{
	m_id;
	m_name;
	m_sex;
}

public List<User> allUser();

-->


<resultMap type="com.po.User" id="userResult">
	<id property="m_id" column="uid"/>
    <result property="m_name" column="uname"/>
    <result property="m_sex" column="usex"/>
    ...
</resultMap>

<select id="allUser" resultMap="userResult">
	select * from user
</select>

级联查询

  1. 一对一
  2. 一对多
  3. 多对多

3种级联关系,级联可以在获取数据的时候提供方便,但是级联过多会增加数据库系统的复杂度,降低系统性能。

更新和删除的级联关系,依靠数据库的内在机制即可完成,而查询的级联关系,我们需要进行级联查询处理。

对于一对一采取多种方式进行SQL查询,比如连接查询,嵌套查询(执行2次SQL)等。主要是进行resultMap的association指定。

对于一对多,与多对多,collection的设置为关键。(多对多转化中间表变一对多)

但是级联查询结果均可以使用POJO来进行自动的映射。

动态SQL

MyBatis的动态SQL元素与JSTL相似,常用:

<if></if>
<choose></choose>
<when></when>
<otherwise></otherwise>
<trim></trim>
<where></where>
<set></set>
<foreach></foreach>
<bind></bind>
<select id="queryUser" resultType="com.po.User" parameterType="com.po.User">
	select * from user where 1=1
    
    <!--if示例-->
    <if test="uname!=null and uname!=''">
    	and uname like concat('%',#{uname},'%')
    </if>
    ...
    <!--choose示例-->
    <choose>
        <when test="uname!=null and uname!=''">
            and uname like concat('%',#{uname},'%')
        </when>
        <otherwise>
            and uid > 10
        </otherwise>
    </choose>
	...
    <!--trim示例 
		prefix,suffix:可以在对应的内容前加上某些前缀和后缀。
 		prefixOverrides,suffixOverrides:忽略某些前缀或后缀
	-->
    <!--构造一个where子句-->
    <trim prefix="where" prefixOverrides="and|or">
        <if test="uname!=null and uname!=''">
            and uname like concat('%',#{uname},'%')
        </if>
    </trim>
    ...
    <!--where示例
		where将生成一个where子句,自动忽略and开头或者or开头
		where内部MyBatis将自动补上空格,所有条件都不满足时会将所有记录都查询出来
	-->
    <where>
        <if test="uname!=null and uname!=''">
            and uname like concat('%',#{uname},'%')
        </if>
    </where>
    
</select>

<!--set 将在update中动态地更新-->

<update id="updateUser" parameterType="com.po.User">
	update user
    <set>
    	<if test="uname!=null and uname!=''">
            uname=#{uname}
        </if>
    </set>
    where uid = #{uid}
</update>


<!--foreach
	item, 集合中的一个元素
	index, 迭代到的下标
	collection, 集合是必选的,可以是List,array,Map
	open, 表示该语句以啥符号开始
	separator, 表示该语句以啥符号进行分隔
	close, 表示以啥符号结束
-->
<select id="queryUserByIdList" resultType="com.po.User" parameterType="List">
	select * from user where uid in
    <!-- (1,2,3,4,5..) -->
    <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
    	#{item}
    </foreach>
</select>



<!--bind-->

<select id="queryUser" resultType="com.po.User" parameterType="com.po.User">
    <!-- 使用bind进行模糊查询,解决不同数据库中模糊查询连接字符不一样的问题 -->
    <bind name="like_param" value="'%'+uname+'%'"/>
	select * from user where uname like #{like_param}
</select>


奖励一下