mybatis小记

mybatis java的持久化框架,和直接使用JDBC的区别是:
使用JDBC需要在代码中写sql语句,返回类型等只能通过JDBC方法来确定,耦合程度高。
mabatis通过约定的mapper接口和config xml来将sql的配置和使用隔离开,清晰的返回类型,方便的连接数据库等等。
本文将就mybatis常用的功能说,更多的关于mybatis的配置请查阅官网

一、mybatis基础配置和增删改查

1.mybatis配置:

resource文件夹下新建mybatis-config.xml,主要是配置在各种环境下通过什么策略来连接数据库(比如dev环境和生产环境的数据库是不同的),以及Mapper类的相应config信息

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="****"/>
                <property name="password" value="****"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="dao/UserMapper.xml"/>
    </mappers>
</configuration>

2.可使用properties字段来创建属性值

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

配置好了properties后,在xml的配置文件中可以使用${}来获取相应的属性,也是将配置和使用隔离开的模式
例如

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

3.创建mybatisUtil工具类来创建Java mybatis数据库连接,主要是获取mybatis config

package utils;

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 MybatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static SqlSession openSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

4.创建dao/UserMapper.java接口以规范Mapper类的行为

package dao;

import java.util.List;

public interface UserMapper {
    List<User> getUsers();
    User getUserByName(String name);
}

5.通过创建dao/UserMapper.xml来配置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">
<mapper namespace="dao.UserMapper">
    <select id="getUsers" resultType="dao.User">
        select * from mybatis.User
    </select>
    <select id="getUserByName" resultType="dao.User">
        select * from mybatis.User where name = #{name}
    </select>
</mapper>

6.App中调用mybatis的函数来获取Mapper并调用相应的方法

SqlSession sqlSession = MybatisUtil.openSqlSession();
        try {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User users = mapper.getUserByName("A");
            System.out.println(users);
//            users.stream().forEach(System.out::println);
        }
        finally {
            sqlSession.close();
        }

可以看出mybatis在简单的增删改查中主要就是配置,首先配置怎么连接数据库,然后读取数据库配置并返回相应Java 连接对象,再通过配置好的Mapper调用相应的方法就可以实现和数据库的交互,整体上实现了配置和使用的分离。mybatis的配置只要一次,Mapper的配置是简单的,多一个sql语句就多一行的配置

7.类型别名

为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>

Mapper的另外用法

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

二、mybatis对java对象和数据库表的映射

使用一个持久层框架时,很关键的一点就是为了实现数据库到内存对象的映射(否则为什么不直接print出来呢),mybatis能够流行的重要原因就是其内置的表项的映射非常友好
mybatis支持如下的映射方式:

1.resultMap映射

把resultMap放在前面是因为复杂的映射仍然需要用resultMap实现,而简单的映射已经可以使用mybatis3中的java注解来实现
resultMap功能强大,可以将非常复杂的表正确的映射到内存对象中
resultMap中存在以下关键字段:

constructor - 用于在实例化类时,注入结果到构造方法中
	idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
	arg - 将被注入到构造方法的一个普通结果
id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result – 注入到字段或 JavaBean 属性的普通结果
association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection – 一个复杂类型的集合
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
discriminator – 使用结果值来决定使用哪个 resultMap
	case – 基于某些值的结果映射

其中constructor, id, result都是非常简单的字段
其通用的匹配方式如下

<result,id,idArg,arg property="*" column="*">

其中property表明了其在java对象中的字段名,column表示其在表中的字段名
而association表明了一个复杂的类型,通常需要进行更进一步的拆分,例如如下的询问

<select id="selectBlog" resultMap="blogResult">
  select
    B.id            as blog_id,
    B.title         as blog_title,
    A.id            as author_id,
    A.username      as author_username,
    A.password      as author_password,
    A.email         as author_email,
    A.bio           as author_bio,
    CA.id           as co_author_id,
    CA.username     as co_author_username,
    CA.password     as co_author_password,
    CA.email        as co_author_email,
    CA.bio          as co_author_bio
  from Blog B
  left outer join Author A on B.author_id = A.id
  left outer join Author CA on B.co_author_id = CA.id
  where B.id = #{id}
</select>

Author的字段如下

<resultMap id="authorResult" type="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
  <result property="password" column="author_password"/>
  <result property="email" column="author_email"/>
  <result property="bio" column="author_bio"/>
</resultMap>

此时resultMap就应该如下,其中association就需要用若干个字段组装起来

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author"
    resultMap="authorResult" />
  <association property="coAuthor"
    resultMap="authorResult"
    columnPrefix="co_" />
</resultMap>

如果觉得resultMap重新列出来非常麻烦,可以使用如下语法,可以不用列出resultMap

<association property="author">
	<id property="id" column="author_id"/>
 	<result property="username" column="author_username"/>
  	<result property="password" column="author_password"/>
  	<result property="email" column="author_email"/>
  	<result property="bio" column="author_bio"/>
</association>

难以想象这种东西在JDBC里面要写多少行,为了将映射关系隔离出来,你可能会需要创建一些工具类以避免代码的耦合,在这些工具类中还需要进行仔细的字段传递,而mybatis很好的解决了这些问题

2.resultMap中的鉴别器(case)

一个鉴别器可以如下

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

鉴别器的作用类似于switch case,可以将表项根据某些字段的结果进行不同对象的封装

3.resultType以及自动映射

mybatis的自动映射将获取到的表和java对象字段进行忽略大小写的,忽略驼峰和蛇形命名法的匹配。有三种自动映射等级:
NONE:禁用自动映射
PARTIAL:对除了连接的属性进行映射
FULL:全自动映射
默认值为PARTIAL,PARTIAL和FULL的区别如下:

<select id="selectBlog" resultMap="blogResult">
  select
    B.id,
    B.title,
    A.username,
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>

<resultMap id="blogResult" type="Blog">
  <association property="author" resultMap="authorResult"/>
</resultMap>

<resultMap id="authorResult" type="Author">
  <result property="username" column="author_username"/>
</resultMap>

由于表中存在id字段,而Auther对象中也存在id字段,因此这两者进行了自动映射,而这显然是不符合我们预期的,因此一般来说用PARTIAL即可

而resultType映射就相当于只是指定了一个java对象并让mybatis进行自动映射

4.java注解映射

常用的映射注解:

@Property
@ConstructorArgs @Arg
@TypeDsicriminator @Case
@Results @Result
@MapKey
@Insert @Update @Delete @Select
@InsertProvide @UpdateProvider @DeleteProvider @SelectProvider
@ResultMap @ResultType @SelectKey

三、动态sql

后端和数据库的交互通常需要附加不同的查询条件

1.if

通过if判断来选择是否加入额外的sql语句

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

2.choose,when,otherwise

类似于switch case语句选择sql语句进行执行

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

3.where, trim, set

当where从句中不存在任何一个结果必定返回时,使用上面的if,choose可能都会很尴尬,但是where标签可以自动去掉最前面的AND,并且只有当子结果中返回至少一个结果时加入WHERE sql语句

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

类似的解决方案也有set

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

如果where和set的行为无法满足要求,可以用定制的trim语句,对where的定制如下,prefix表示满足某个条件时加入的sql语句,prefixoerrides表示要删掉的前缀,文档提到这里的AND 必须要加空格,应该是为了满足管道分隔符的格式

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

4.foreach

foreach主要是用来判断表项是否在某个集合中的,其中item和index用来描述java对象的内容和下标,collection表示集合类型,open后的内容指定sql的内容,包括

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  <where>
    <foreach item="item" index="index" collection="list"
        open="ID in (" separator="," close=")" nullable="true">
          #{item}
    </foreach>
  </where>
</select>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值