Mybatis

 

概念:Mybatis是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架。
          Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
优缺点:
            优点:(1)易于上手和掌握。
                      (2)sql写在xml里,便于统一管理和优化。
                      (3)解除sql和程序代码的耦合。
                      (4)支持编写动态sql
            缺点:(1)sql工作量很大。尤其是字段多,关联表多时。
                      (2)sql依赖于数据库,导致数据库移植性差。
                      (3)由于xml里标签id必须唯一。导致DAO中方法不支持重载。
                      (4)编写动态SQL时不方便调试。
Mybatis是半自动的,自动化的地方在与把数据给我们封装好返回给我们,手动的部分在于我们需要自己写sql。这样的方式比较灵活,可以根据需求写最优的sql.
Hibernate是全自动的,sql都帮我们写好了,但这也是hibernate的一大缺点。有时候他的sql语句并不是最优的,极大的影响效率。

一.mybatis简单的配置
使用最基础最常用的xml形式进行配置。
1.在src/main/resource下面创建mybatis-config.xml文件。配置如下:
<configuration>
    <settings>
        <!--配置使用LOG4j输出日志-->
        <setting name="logImpl" name="LOG4j"></setting>
    </settings>

    <typeAliases>
        <!--配置别名-->
        <package name="com.hy.credit.entity"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type = "JDBC">
                    <property name="" value="">
            </transactionManager>
            <!--配置数据源-->
            <datasource type="UNPOOLED">
                <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>
</configuration>
2.创建实体类和Mapper.xml文件
public class County{
    private Long id;
    private String countryname;
    private String countrycode;
}

<mapper namespace = "com.hy.credit.mapper.countryMapper">
    <select id="selectAll" resultType="Country">
        select id,countryname,countrycode from country
    </select>
</mapper>

3.配置日志参数以查看mybatis操作数据库的过程
log4j.properties
#全局配置
    log4j.rootLogger = ERROR,stdout
#mybatis日志配置
    log4j.logger.com.hy.credit.mapper=TRACE
#控制台输出配置
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n  

4.编写测试代码让mybatis跑起来

二、mybatis配置文件配置介绍
1.sqlSessionFactoryBuilder源码片段
通过调用SqlSessionFactoryBuilder.build()方法获取SqlSessionFactory.具体build方法中是如何获取的呢?
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        //1.通过传入的mybatis-config.xml构造出XMLConfigBuilder对象。
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            reader.close();
        } catch (IOException var13) {
        }

    }

    return var5;
}
那XMLConfigBuilder在构造时又做了什么呢?看一下XMLConfigBuilder的构造方法。
XMLConfigBuilder.java
//2.构造XMLConfigBuilder
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
//3.构造XPathParser
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    this.localReflectorFactory = new DefaultReflectorFactory();
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}
//4.解析返回Configuration
public Configuration parse() {
    if (this.parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
        this.parsed = true;
        this.parseConfiguration(this.parser.evalNode("/configuration"));
        return this.configuration;
    }
}
//5.解析xml
private void parseConfiguration(XNode root) {
    try {
        this.propertiesElement(root.evalNode("properties"));
        Properties settings = this.settingsAsProperties(root.evalNode("settings"));
        this.loadCustomVfs(settings);
        this.loadCustomLogImpl(settings);
        this.typeAliasesElement(root.evalNode("typeAliases"));
        this.pluginElement(root.evalNode("plugins"));
        this.objectFactoryElement(root.evalNode("objectFactory"));
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
        this.settingsElement(settings);
        this.environmentsElement(root.evalNode("environments"));
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
}
***********************************************************************************************************************************
XPathParser.java
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    this.commonConstructor(validation, variables, entityResolver);
    this.document = this.createDocument(new InputSource(reader));
}

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
}

(1)解析properties标签
     将<properties>下的<property>属性加载,之后读取resource配置文件或者配置url的地址。但是resource和url不能同时配置,会报错。最后set进configuration中。
(2)解析Setting标签
    获取<settings>下配置的<setting>,调用settingsElement()方法,去设置对应属性。
(3)解析typeAliases标签
 如果配置的是package节点,mybatis会自动扫描指定包下的实体类,并且默认设置一个别名,首字母小写。
 如果配置的是typeAlias节点,那么就设置alias和type属性。alias:别名,type:对应的实体类。
 其实就是通过一个HashMap实现的,key为别名,value为别名对应的类型(class对象)
(4)解析environments标签
environments元素节点可以配置多个environment子节点。可以设置两个environment,两个id分别对应开发环境(dev)和正式环境,那么通过配置environments的default属性就能选择对应的environment。
(5)解析typeHandlers标签
mybatis在预处理语句设置参数时调用TypeHandler进行java对象到jdbc的prepareStatement参数值的转换。
mybatis查询数据库完成后,调用TypeHandler的方法读取数据转换成java对象。
可以自定义
(6)mappers标签
这个节点下,配置我们的mapper映射文件。所谓的mapper映射文件,是让mybatis用来建立数据表与javabean映射的一个桥梁。实际开发中,一个mapper文件对应一个dao接口,mapper可以看成是对应dao的实现。
mapper的配置方式
<mappers>
   <!--第一种方式  通过resource指定-->
    <mapper resource="com/hy/mybatis/mapper/CountryMapper.xml"/>
    <!--第二种方式 通过class指定接口,进而能够将对应的xml文件和接口形成映射关系。
        使用这种方式必须保证 接口与mapper文件同名(不区分大小写)
        这配置 CountryMapper,那么意味着mapper文件为CountryMapper.xml
    -->
    <mapper class="com.hy.mybatis.mapper.CountryMapper"/>
    <!--第三种方式 直接指定包,自动扫描  与方法二同理-->
    <package name="com.hy.mybatis.mapper"/>
    <!--第四种方式 通过url指定mapper文件位置-->
    <mapper url="file://........................"/>
</mappers>

三、mapper文件配置之insert、update、delate。
<!--mapper 为根元素节点,一个namespace对应一个dao-->
<mapper namespace = "com.hy.mybatis.mapper.CountryMapper">
    <!--id(必须配置)
        1.id是命名空间中的唯一标识符,可被用来代表这条语句
        一个命名空间对应一个dao接口
        这个id也应该对应dao里面的某个方法,因此id应该与方法名一致
    -->
    <!--2.parameterType(可选配置,默认mybatis自动选择处理)
          将要传入语句的参数的完全限定类名或别名。如果不配置,mybatis会通过ParameterHandler根据参数类型默认选择合适的typeHandler进行处理。parameterType 主要指定参数类型,可以是int,short,long,String等类型,也可以是复杂类型。  
    -->
    <!--3.flushCache(可选配置,默认配置为true)
           将其设置为true,任何时候 只要语句被调用,都会导致本地缓存和二级缓存都会被清空。对应 插入、更新和删除
    -->
    <!--4.statementType(可选配置,默认配置为prepared)
        标记使用什么对象操作sql
        (1)Statement:直接操作sql,不进行预编译,获取数据 
        (2)Prepared:预处理,参数,进行预编译,获取数据。(默认的)
        (3)Callable:执行存储过程
    -->
    <!--5.useGeneratedKeys(可选配置,默认为false)
        仅对insert和update有用。这会让mybatis使用JDBC的getGeneratedKeys方法来取出数据库内部生成的键。默认为false.
        当设置为true时,表示如果插入的表以自增列为主键,则允许JDBC支持自动生成主键,并可将自动生成的主键返回。    
    -->
    <!--6.keyProperty(可选配置,默认不设置)
        仅对insert和update有用。唯一标记一个属性。mybatis会通过getGeneratedKeys的返回值,或者insert语句的selectKey元素设置它的值。 
         userGeneratedKeys和selectkey的区别。
         useGeneratedKeys要求数据库本身具备主键自动增长的功能。比如像mysql,sqlserver。不支持主键自动增长的数据库是不能用这个属性的。比如oracle
         不支持的就用selectkey来做,像uuid,或者序列。         
    -->
    <!--7.timeout(可选配置,默认不设置,依赖驱动)
        这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。
    -->
    <insert id="insert" parameterType="com.hy.credit.entity.CreditRoot" useGeneratedKeys="true" keyProperty="id" >
        </insert>
</mapper>

四、mapper文件配置之select,resultMap
<select
    <!--id(必须配置) id是命名空间中的唯一标识符,可被用来代表这个语句-->
    id="selectPerson"
    <!---->
    <!--parameterType(可选配置,默认为mybatis自动处理)-->
    parameterType = "int"
    <!--resultType(resultType和resultMap 二选一配置) 用于指定返回类型 可以是基本类型,可以是javabean-->
    resultType = "hashMap"
    <!--resultMap(resultType和resultMap 二选一配置) resultMap用于引用我们通过resultMap标签定义的映射类型。-->
    resultMap= "personResultMap"
    <!--flushCache(可选配置,默认为false) 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存清空。-->
    flushCache = "false"
    <!--useCache(可选配置,默认为true)-- 设置为true,会导致本条语句的结果被二级缓存。>
    useCache = "true"
    <!--timeout(可选配置) 抛出异常之前,等待数据库返回请求结果的秒数-->
    timeout ="100"    
    <!--fetchSize 这是一个给驱动的提示,尝试让驱动程序每次批量返回结果行数和这个设置值相等 默认未设置-->
    fetchSize = "256" 
    <!--statementType(可选配置) 默认为prepared-->
    statementType = prepared

</select>

五、强大的动态SQL
传统的使用JDBC的方法,在组合复杂SQL语句的时候,需要去一步一步的拼接。稍不注意少个空格,都会导致错误。Mybatis的动态SQL正是为了解决这种问题。
通过if,choose,when,otherwise,trim,where,set,foreach标签,可组成非常灵活的SQL语句,提高开发效率。
(1)if标签:
<select id="findUserById" resultType = "User">
    select * from user where
    <if test ="id != null">
        id = #{id}
    </if>
    and deleteFlag = 0;
</select>
如果id传入的不为空,SQL会拼接上id = #{id}.
但是id传入的为空呢,SQL语句就变成了select * from user where and deleteFlag = 0;这不对啊,这时候where标签就出来了。
(2)where标签:
<select id="findUserById" resultType="user">
    select * from user
    <where>
        <if test ="id != null">
            id = #{id}
        </if>
        and deleteFlag = "0";
    <where>
</select> 
mybatis对where标签做了处理,当它遇到or 或者and的时候知道怎么去处理。我们也可以通过trim标签去自定义这种处理规则
(3)trim标签:
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <trim prefix="WHERE" prefixOverrides="AND">
    <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>
    </trim>
</select>

trim的四个属性:
    prefix:给sql语句拼接的前缀
    suffix:给sql语句拼接的后缀
   prefixesToOverride:去除sql语句前面的关键字或者字符
   suffixesToOverride:去除sql语句后面的关键字或者字符
(4)set标签
<update id="updateUser" parameterType="com.dy.entity.User">
           update user set
           <if test="name != null">
               name = #{name},
           </if>
           <if test="password != null">
               password = #{password},
           </if>
           <if test="age != null">
               age = #{age}
           </if>
           <where>
               <if test="id != null">
                   id = #{id}
               </if>
               and deleteFlag = 0;
           </where>
</update>
如果之后name不为null,这个SQL就会变成update set name = #{name},where .... 是有问题的,改造之后的呢?
<update id="updateUser" parameterType="com.dy.entity.User">
           update user
        <set>
            <if test="name != null">name = #{name},</if>
             <if test="password != null">password = #{password},</if>
             <if test="age != null">age = #{age},</if>
        </set>
           <where>
               <if test="id != null">
                   id = #{id}
               </if>
               and deleteFlag = 0;
           </where></update>

这样set标签就会自动处理。
(5)foreach标签
 foreach元素主要用在构建in条件中,可以在SQL语句中进行迭代一个集合。
主要属性有item,index,collection,open,separator,close.
item:表示集合中每个元素在迭代时的别名。
index:表示每个元素在迭代时的位置。
open:表示语句以什么开始。
separator:表示在每次迭代之间以什么符号作为分隔符
close:表示以什么结束。
collection:最容易出错的属性。分为三种:
        1.如果传入的是单参数,且参数类型为一个List的时候,collection属性值为list。(a,b)
        2. 如果传入的是单参数,且参数类型为一个array数组的时候,collection的属性值为array。
        3.如果传入的参数是多个的时候,我们就需要封装成一个Map。当然单参数也可以。
<select id="dynamicForeach3Test" parameterType="java.util.HashMap" resultType="Blog">
2         select * from t_blog where title like "%"#{title}"%" and id in
3          <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
4               #{item}
5          </foreach>
6 </select>
(6)choose标签
    
<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title = #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose></select>
如果title和author都不为null的时候,二选一,前者优先。
六、mybatis缓存配置
使用缓存可以使应用更快的获取数据,避免频繁的数据库交换,尤其是再查询越多,缓存命中率越高的情况下,使用缓存的作用就越明显。
mybatis作为持久化框架,提供了非常强大的查询缓存特性,可以非常方便的配置和定制使用。
一般提到mybatis缓存的时候,都是值二级缓存,一级缓存默认会启用。(整合Spring后会失效)
1.一级缓存
通过例子看一下一级缓存如何起作用:
public void testLiCache(){
    //获取sqlSession
    SqlSession sqlSession = getSqlSession();
    SysUser user1 = null;
    //获取userMapper接口
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用selectById方法,查询id =1 的用户
    user1 = userMapper.selectById(1);
    //对当前获取的对象重新赋值
    user1.setUserName("New Name");
    //再次查询id = 1的用户
    SysUser user2 = userMapper.selectById(1);
    String userName = user2.getUserName();
    Boolean flag = user1 == user2;
}

mybatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象。
在使用mybatis的过程中,要避免出现上述的错误。如果不想让selectById使用一级缓存,可以修改mapper.xml中,<select flushCache = "true">更改flushCache的属性值,配置为true后,会在查询数据前清空当前的一级缓存,因此该方法每次都会重新从数据库中查询数据。但是如果清空了一级缓存,会反复查询数据库中的数据,会增加数据库的查询次数,所以尽量避免这样使用。
2.二级缓存
mybatis的二级缓存非常强大,它不同于一级缓存只存在与SqlSession的生命周期中,而是可以理解为存在与SqlSessionFactory的生命周期中。
1.配置二级缓存。
在mybatis的全局配置settings中有一个参数cacheEnabled,这个参数是二级缓存的全局开关,默认是true,初始状态为启用状态。如果把这个参数设置为false,即使有后面的二级缓存配置,也不会生效。mybatis的二级缓存是和命名空间绑定的,即二级缓存需要配置在Mapper.xml映射文件中,或者配置在Mapper.java接口中。在映射文件中,命名空间就是xml根节点的namespace属性。在Mapper接口中,命名空间就是接口的全限定名称。
2.Mapper.xml中配置二级缓存。
在保证二级缓存的全局配置开启的情况下,给Mapper.xml开启二级缓存只需要在Mapper.xml中添加<cache/>元素即可。
<mapper namespace = "com.hy.mybatis.mapper.UserMapper">
    <cache/>
</mapper>
默认的二级缓存会有如下效果。
(1) 映射语句文件中的所有select语句将会被缓存。
(2) 映射语句文件中的所有insert,update,delete语句会刷新缓存
(3) 缓存会使用Least Receently Used(LRU,最近最少使用的)算法来收回。
(4) 缓存会存储集合或对象的1024个引用。
3.Mapper接口中配置二级缓存。
在使用注解方式时,如果想要对注解方法启用二级缓存,需要在Mapper接口中进行配置。
@CacheNamespace
public interface UserMapper{
}

mybatis默认提供的缓存实现是基于Map实现的内存缓存,已经可以满足基本的应用了。但是需要缓存大量的数据时,不能仅仅通过提高内存来使用mybatis的二级缓存,需要选择一些如redis缓存数据库等工具来保存mybatis的二级缓存数据。

3.集成Redis缓存。
Redis是一个高性能的key-value 数据库
Mybatis缓存通过org.apache.ibatis.cache.Cache实现,利用Redis做mybatis的二级缓存需要实现这个接口。
重写Cache方法,在Mapper.xml中的<cache type="">配置上自定义缓存的类。

4.缓存穿透、缓存雪崩、缓存击穿
缓存穿透:是指查询一个数据库一定不存在的数据。
做缓存的一个流程:
(1)参数传入对象主键Id
(2)根据key从缓存中获取对象
(3)如果对象不为空,直接返回。如果对象为空,则进行数据库查询
(4)如果从数据库查询出的对象不为空,则放入缓存。为空呢?
这样暴露的一个问题就是每次查询都是空,每次又不会缓存,利用这个,对数据库造成压力。
解决办法就是查出的空值,也放入缓存,缓存过期时间设置的较短。
缓存雪崩:是指在某一个时间段,缓存集中过期失效。
这样在某一个时间段,就会集中的访问数据库,对数据库而言是产生周期性的压力波峰。
尽可能根据业务分散过期时间。
这还不是更致命的,致命的是缓存服务器某个节点宕机了。这个后果是不可预知的。
缓存击穿:是值一个key非常热点,不停的扛着大并发,当这个key在失效的瞬间,持续的大并发就会直接请求数据库。
解决办法:对这种key 设置缓存永不过期或者互斥锁。
七、Spring集成Mybatis
1.引入mybatis-spring依赖
<dependecy>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>
2.配置SqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSesionFactoryBean">
    <property name="configLocation" value=""/>
    <property name = "dataSource" value+""/>
        <property name = "mapperLocations">
        <property name = "typeAliasesPackage">
</bean>
3.编写对应mapper接口和mapper.xml

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值