学习资源

【转】MyBatis的配置和使用原理

2017-06-18  本文已影响157人  王帅199207

MyBatis的配置和使用原理


MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库 中的记录。

MyBatis让程序将主要精力放在sql上,通过MyBatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。

MyBatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)

MyBatis框架原理


MyBatis框架执行过程:

新建一个工程用于描述这些用法。

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    // 属性名和数据库表的字段对应
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

    // 用户创建的订单列表
    private List<Orders> ordersList;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address="
                + address + "]";
    }

    public List<Orders> getOrdersList() {
        return ordersList;
    }

    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
}

编写User.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">

<!-- namespace命名空间,作用就是对sql进行分类化管理,理解sql隔离 
注意:使用mapper代理方法开发,namespace有特殊重要的作用
-->
<mapper namespace="test">

    <!-- 在 映射文件中配置很多sql语句 -->
    <!-- 需求:通过id查询用户表的记录 -->
    <!-- 通过 select执行数据库查询
    id:标识 映射文件中的 sql
    将sql语句封装到mappedStatement对象中,所以将id称为statement的id
    parameterType:指定输入 参数的类型,这里指定int型 
    #{}表示一个占位符号
    #{id}:其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称

    resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
     -->
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
        SELECT * FROM USER WHERE id=#{value}
    </select>

    <!-- 根据用户名称模糊查询用户信息,可能返回多条
    resultType:指定就是单条记录所映射的java对象 类型
    ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
    使用${}拼接sql,引起 sql注入
    ${value}:接收输入 参数的内容,如果传入类型是简单类型,${}中只能使用value
     -->
    <select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
        SELECT * FROM USER WHERE username LIKE '%${value}%'
    </select>

    <!-- 添加用户 
    parameterType:指定输入 参数类型是pojo(包括 用户信息)
    #{}中指定pojo的属性名,接收到pojo对象的属性值,MyBatis通过OGNL获取对象的属性值
    -->
    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
        <!-- 
        将插入数据的主键返回,返回到user对象中

        SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键

        keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
        order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
        resultType:指定SELECT LAST_INSERT_ID()的结果类型
         -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
        <!-- 
        使用MySQL的uuid()生成主键
        执行过程:
        首先通过uuid()得到主键,将主键设置到user对象的id属性中
        其次在insert执行时,从user对象中取出id属性值
         -->
        <!--  <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
        </selectKey>
        insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address}) -->


    </insert>

    <!-- 删除 用户
    根据id删除用户,需要输入 id值
     -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!-- 根据id更新用户
    分析:
    需要传入用户的id
    需要传入用户的更新信息
    parameterType指定user对象,包括 id和更新信息,注意:id必须存在
    #{id}:从输入 user对象中获取id属性值
     -->
    <update id="updateUser" parameterType="cn.itcast.mybatis.po.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 
         where id=#{id}
    </update>

</mapper>

配置SqlMapConfig.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>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由MyBatis-->
            <transactionManager type="JDBC" />
        <!-- 数据库连接池,由MyBatis管理-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 加载 映射文件 -->
    <mappers>
        <mapper resource="sqlmap/User.xml"/>

        <!--通过resource方法一次加载一个映射文件 -->
        <!-- <mapper resource="mapper/UserMapper.xml"/> -->

        <!-- 通过mapper接口加载单个 映射文件
        遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录 中
        上边规范的前提是:使用的是mapper代理方法
         -->
        <!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper"/> -->

        <!-- 批量加载mapper
        指定mapper接口的包名,MyBatis自动扫描包下边所有mapper接口进行加载
        遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录 中
        上边规范的前提是:使用的是mapper代理方法
         -->
        <package name="cn.itcast.mybatis.mapper"/>

    </mappers>
</configuration>

查找

对应的User.xml中的statement

模糊查找

对应的User.xml中的statement

插入

对应的User.xml中的statement

删除

对应的User.xml中的statement

修改

对应的User.xml中的statement

parameterType

在映射文件中通过parameterType指定输入 参数的类型。
对于综合查询,建议parameterType使用包装的pojo,(将各种pojo包装在一个单独的类)有利于系统 扩展。

resultType

在映射文件中通过resultType指定输出结果的类型。查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。

reusltMap

可以通过resultMap 完成一些高级映射。

如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。

高级映射:
将关联查询的列映射到一个pojo属性中。(一对一)
将关联查询的列映射到一个List<pojo>中。(一对多)

#{}和${}
selectOne和selectList

selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。

selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。
如果使用selectOne会报错:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

MyBatis和Hibernate本质区别和应用场景


企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。

MyBatis开发dao的方法:

(1)、原始dao 的方法

需要程序员编写dao接口和实现类
需要在dao实现类中注入一个SqlSessionFactory工厂。

(2)、mapper代理开发方法(建议使用)

只需要程序员编写mapper接口(就是dao接口)
程序员在编写mapper.xml(映射文件)和mapper.java需要遵循一个开发规范:
1、mapper.xml中namespace就是mapper.java的类全路径。
2、mapper.xml中statement的id和mapper.java中方法名一致。
3、mapper.xml中statement的parameterType指定输入参数的类型和mapper.java的方法输入 参数类型一致。
4、mapper.xml中statement的resultType指定输出结果的类型和mapper.java的方法返回值类型一致。

查询缓存


MyBatis提供查询缓存,用于减轻数据压力,提高数据库性能。
MyBatis提供一级缓存,和二级缓存。

数据结构 (HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

为什么要用缓存?
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

spring 进行整合开发,事务控制在service中。
一个service方法中包括 很多mapper方法调用。

如果是执行两次service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

首先开启MyBatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache=”true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

分布式缓存框架Ehcache

Ehcache是一个分布式缓存框架。(现在仅介绍单机应用在MyBatis,分布式应用的研究后续补上)

我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。

MyBatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

MyBatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

MyBatis和ehcache整合,MyBatis和ehcache整合包中提供了一个cache接口的实现类。

Ehcache与MyBatis整合

配置mapper中cache中的type为ehcache对cache接口的实现类型。

二级缓存应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用MyBatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

逆向工程


MyBatis需要程序员自己编写sql语句,MyBatis官方提供逆向工程 可以针对单表自动生成MyBatis执行所需要的代码(mapper.java,mapper.xml、po..)

企业实际开发中,常用的逆向工程方式:
由数据库的表生成java代码。

代码自动生成配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
            password="mysql">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
            NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="cn.itcast.ssm.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="cn.itcast.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.itcast.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="items"></table>
        <table tableName="orders"></table>
        <table tableName="orderdetail"></table>
        <table tableName="user"></table>

        <!-- 有些表的字段需要指定java类型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>

自动生成代码的代码。

打开UserMapper.java,默认生成的Mapper映射接口就是下面这么多。

上一篇下一篇

猜你喜欢

热点阅读