Spring整合MyBatis

MyBatis是一个半自动的ORM框架,它要求开发者编写具体的SQL语句。
MyBatis解决的问题:
JDBC使用复杂,需要操作Connection、Statement、ResultSet等对象,并要处理异常、正确关闭资源等。
MyBatis是一种ORM模型。ORM简单来说就是数据库表和JAVA对象相互映射

一.配置MyBatis

每个MyBatis的应用程序都以一个SqlSessionFactory对象实例为核心。这个对象由SqlSessionFactoryBuilder从XML配置文件或Configuration(?)类的实例中构建SqlSessionFactory对象。

1.1在Spring中配置MyBatis

  1. 将mybatis-spring依赖在pom.xml中引入

  2. 在Mybatis.xml配置dataSource、sqlSessionFactory、MapperScannerConfigurer
    注:配置文件的名字可以自己起,不一定是MyBatis.xml

dataSource:数据源,这个只要连接数据库都要配置

sqlSessionFactory:注入数据源,配置文件,sql映射文件扫描位置。注意mapperLocations属性,用它说明sql映射文件的存储位置,不用再依次列出每个文件了,新添加映射文件时也不用再做修改。

MapperScannerConfigurer:这是mybatis-spring提供的一个转换器,可以将映射接口转换为Spring容器中的Bean,这就是为什么我们只定义了dao层接口并没有实现,却可以在service层直接注入dao的原因。MapperScannerConfigure将扫描basePackage包下的所有接口,如果它们在sql映射文件中定义过,则将它们动态定义为一个Spring Bean。

下面是一个配置的具体例子:

<!-- 添加连接池则改变数据源 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

    <!-- spring和MyBatis整合,不需要在mybatis的配置文件中写每个entity的映射文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!-- 自动扫描mapper.xml文件 -->
        <property name="mapperLocations" value="classpath:sqlmapper/*.xml"></property>
    </bean>

    <!-- mapper接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.edu.xidian.see.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

对于将映射接口,转换为可以使用的实例,Spring还提供了SqlSessionTemplate,可以通过其getMapper(Class<T> type)来获得一个实例,通过这个实例即可调用sql映射文件定义的映射项。

二.使用MyBatis

使用MyBatis访问数据库可以分为三步:

  1. 定义Mapper映射接口(java接口)。Sql映射文件通过namespace和Mapper接口一一对应,每个select\update\insert等标签对应一个接口中的方法。。
  2. 定义MyBatis Sql映射文件。ResultMap,动态SQL
  3. 在Service层等调用处,注入Java接口。因为MapperScannerConfigurer已经将接口映射为Spring Bean实例,可以直接使用Autowire注入Dao。

sql映射文件中的动态SQL一般用于拼接SQL语句,主要有一下几种

  1. <if>
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
    select * from user where
        <if test="username != null">
           username=#{username}
        </if>
         
        <if test="sex!= null">
           and sex=#{sex}
        </if>
</select>
  1. <if> + <where>
    上述<if>的例子中,如果username==null,sex!=null,就会拼接出错误的sql语句。将条件放入where标签中,它将去掉多余的and,如果返回的内容为空的话,它也不会插入‘where’
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
    select * from user
<where>
        <if test="username != null">
           username=#{username}
        </if>
         
        <if test="sex!= null">
           and sex=#{sex}
        </if>
</where>
</select>

3.适用于update语句的<set> <if>组合
如果需求是:根据属性是否为null来决定是否更新字段那么,可以用此组合。
如果有多余的“,”set标签会删除掉

<update id="updateUserById" parameterType="com.ys.po.User">
    update user u
        <set>
            <if test="username != null and username != ''">
                u.username = #{username},
            </if>
            <if test="sex != null and sex != ''">
                u.sex = #{sex}
            </if>
        </set>
     
     where id=#{id}
</update>

4.choose(when,otherwise)语句
依次判断when的条件,当有一个满足时,结束判断,当都不满足是使用otherwise中的语句。

<select id="selectUserByChoose" resultType="com.ys.po.User" parameterType="com.ys.po.User">
      select * from user
      <where>
          <choose>
              <when test="id !='' and id != null">
                  id=#{id}
              </when>
              <when test="username !='' and username != null">
                  and username=#{username}
              </when>
              <otherwise>
                  and sex=#{sex}
              </otherwise>
          </choose>
      </where>
  </select>

5.trim语句
可以实现where或set的功能,是更一般化的标签,可以指定开头(prefix)、结尾(suffix)、开头处的多余字符(prefixOverrides)、结尾处的多余字符(suffixOverrides)

<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
        select * from user
        <trim prefix="where" prefixOverrides="and | or">
            <if test="username != null">
               and username=#{username}
            </if>
            <if test="sex != null">
               and sex=#{sex}
            </if>
        </trim>
    </select>
   <update id="updateUserById" parameterType="com.ys.po.User">
        update user u
            <trim prefix="set" suffixOverrides=",">
                <if test="username != null and username != ''">
                    u.username = #{username},
                </if>
                <if test="sex != null and sex != ''">
                    u.sex = #{sex},
                </if>
            </trim>
         
         where id=#{id}
    </update>

6.foreach语句
实现遍历list。

<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User">
        select * from user
        <where>
            <!--
                collection:指定输入对象中的集合属性
                item:每次遍历生成的对象
                open:开始遍历时的拼接字符串
                close:结束时拼接的字符串
                separator:遍历对象之间需要拼接的字符串
                select * from user where 1=1 and id in (1,2,3)
              -->
            <foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
                #{id}
            </foreach>
        </where>
    </select>

三.MyBatis运行原理

MyBatis的核心组件:
SqlSessionFactoryBuilder:生成SqlSessionFactory
SqlSessionFactory:生成SqlSession
SqlSession:发送SQL去执行,并返回结果

MyBatis的运行包括两部分:一。读取配置文件,构建SqlSessionFactory对象。二。SqlSession的执行过程。以下分析较为复杂的第二部分。

先看一个问题:使用MyBatis时需要定义Mapper接口和SQL映射文件,这里的Mapper只是个接口,他是如何执行的?

答案就是动态代理。

动态代理有两种实现:1是JDK通过反射提供的动态代理;2是CGLIB动态代理。区别:JDK代理需要提供接口,CGLIB不需要;MyBatis中这两种方式都使用了。

一般来说实现代理,代理的是一个目标类。但是MyBatis使用时并没有类,只有一个接口。这也是可以的。JDK动态代理可以直接生成 一个接口的实现类。

JDK动态代理的使用:

//首先是实现InvocationHandler接口,实现invoke方法
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
  //method是要被代理执行的方法
  //args是要传入的参数
  //被代理类方法的执行:
  
  Object result = method.invoke(args);
  
//可加入其他逻辑
}

//二是要生成代理类,使用Proxy.newProxyInstance()
//这里的传入的第三个参数this应该是一个实现InvocationHandler接口的类的对象
//这个例子是还是先有了一个目标类,但只有接口的情况也是可以的
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

//只有接口的情况

CGLIB

public class HelloServiceCGLib implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理之前");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("代理之后");
        return result;
    }

    private Object target;

    public Object getProxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
}

//Main.java
public class Main {
    public static void main(String[] args) {
        HelloServiceCGLib helloServiceCGLib = new HelloServiceCGLib();
        HelloService target = new HelloServiceImpl();
        HelloService helloService = (HelloService) helloServiceCGLib.getProxy(target);
        helloService.sayHello("xiaoming");
    }
}

动态代理实现原理

JDK动态代理:
利用反射实现;
只依赖JDK本身,不需要依赖外部库;JDK比外部库更加可靠;
需要被代理类实现接口;
代码实现简单一些。

CGLIB动态代理:
基于ASM实现;
不需要目标类实现接口;
不能代理final类,因为不能生成其子类;
高性能。

反射:
涉及Class,Field,Method,Constructor等类

SqlSessionFactory的构建过程,略

主要是读取配置文件到Configuration类,在通过SqlSessionFactoryBuilder类生成

SqlSession的执行流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容