Mybatis

Mybatis

概念

  • SqlSession
    can execute commands, get mappers and manage transactions, Closing the session is very important
  • SqlSessionFactory instance
    can create instances of SqlSessions all different ways, should exist for the duration of your application execution
  • SqlSessionFactoryBuilder
    can create the SqlSessonFactory from XML, Annotations or hand coded Java configuration
  • MapperFactoryBean
String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
} finally {
  session.close();
}
  1. Transaction use a transaction scope for the session, or use auto-commit
  2. Connection
  3. Execution reuse PreparedStatements and/or batch updates

全局配置

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods"
    value="equals,clone,hashCode,toString"/>
</settings>

typeAliases

  • 可以挨个指定或者typeAliasesPackage指定目录(没使用@Alias("author")指定bean名的话,就使用bean类名作为别名)
  • built-in type aliases for common Java types 见详细文档
    别名和java基本类型对应关系:
    91fabf5f.png

基本类型和数据库类型的对应关系:


97f21b4a.png

plugins

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

transactionManager

  • JDBC
  • MANAGED does almost nothing

dataSource

  • UNPOOLED
  • POOLED
  • JNDI intended for use with containers such as EJB or Application Servers

元素

<!--  be used to define a reusable fragment of SQL code -->
<sql id="queryCondition">
</sql>

<insert id="insertPostUpload" parameterType="ForumAppModel" >
<!-- 获取id -->
        <selectKey resultType="java.lang.String"  <!-- STATEMENT, PREPARED or CALLABLE -->
      order="BEFORE"   <!-- BEFORE or AFTER -->
            keyProperty="id"  <!-- STATEMENT, PREPARED or CALLABLE -->
      >
            SELECT seq_user_forum_app.nextval AS ID FROM
            DUAL
        </selectKey>
</insert>


<select
  id="selectPerson"
  parameterType="int" 
  resultType="hashmap"
  flushCache="false"  <!-- cause the local and 2nd level caches to be flushed whenever this statement is called -->
  useCache="true"   <!-- cause the results of this statement to be cached in 2nd level cache -->
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"  <!-- STATEMENT, PREPARED or CALLABLE -->
  resultSetType="FORWARD_ONLY">

statementType

STATEMENT:

  1. 普通的不带参的查询SQL;
  2. 支持批量更新,批量删除;
  3. 由于每次都是拼接sql,在DB层面没有缓存的必要

PREPARED:

  1. 可变参数的SQL,会预编译存储在DB的预编译缓存区域;编译一次,执行多次,效率高;
  2. sql代码可读性较高;
  3. 安全性好,有效防止Sql注入等问题;
  4. 支持批量更新,批量删除;

CallableStatement:

  1. 继承自PreparedStatement,支持带参数的SQL操作;
  2. 支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;

jdbcType:

5814e721.png

cache

logging

MyBatis provides logging information through the use of an internal log factory

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

SLF4J, LOG4J, LOG4J2, JDK_LOGGING, COMMONS_LOGGING, STDOUT_LOGGING, NO_LOGGING or a full qualified class name that implements org.apache.ibatis.logging.Log


基于Spring的mybatis

dependency injection framework(DI)
sqlSession直接由依赖注入框架直接创建,can create thread safe, transactional SqlSessions and mappers and inject them directly into your beans

mybatis-spring
in the Spring application context: an SqlSessionFactory and at least one mapper interface

  1. 创建一个挂接datasource的sqlSessionFactory(在spring下由SqlSessionFactoryBean代理)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
    <!-- bean命名,通配 -->
    <property name="typeAliasesPackage" value="com.cssweb.*.model,com.gtja.*.model,com.cssweb.*.pojo,com.dzsw.*.model" />
    <!-- mapper.xml文件,通配 -->
    <property name="mapperLocations" value="classpath*:com/**/mapper/*Mapper.xml" />
    <!-- 日志转交log4j管理 -->
    <property name="configLocation" value="classpath:config/mybatis-config.xml"/> 
</bean> 
  1. 定义一个mapper接口,可直接注入sql或者在xml文件
//直接注入SQL
public interface UserMapper {
  @Select("SELECT * FROM users WHERE id = #{userId}")
  User getUser(@Param("userId") String userId);
}
  1. 将mapper接口加入spring,定义MapperFactoryBean
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

<!-- 替换3方案的方案是:MapperScannerConfigurer,可以使用通配符 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.*.*.mapper"></property>
</bean>
  1. 由MapperFactoryBean对sqlsession进行管理,其异常传递到DataAccessExceptions

  2. DataSourceTransactionManager
    transactionManager allows MyBatis to participate in Spring transactions

<!-- mybatis直接使用JDBC的事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务驱动 -->
<tx:annotation-driven transaction-manager="txManager"
        order="1" />
  1. mapper.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有用,和接口保持一致 -->
<mapper namespace="com.gtja.forumApp.mapper.ForumAppMapper">
  <select id ="getEmployeeName" parameterType="String" resultType="String">
          select t.REAL_NAME creater from user_basic_info t where t.EMPLOYEE_ID=#{employeeId}
  </select>
</mapper>

注:

  • 在classpath后面的必不可少,缺少的话后面的通配符不起作用。
  • **表示可以表示任意多级目录
  • *表示多个任意字符
  • 一个通用的属性是 configLocation,它是用来指定 MyBatis 的 XML 配置文件路径的。 如果基本的 MyBatis 配置需要改变, 那么这就是一个需要它的地方。 通常这会是<settings> 或<typeAliases>的部分

动态SQL

#{}与${}的区别:#{}会根据当前值类型进行处理,而${}则替换当前位置,容易出现sql注入;

sql注入:

密码框中输入的是' ' or 1 = 1 加了一引号和一个恒成立的条件,那么,传到数据库中的sql就是:
select count(1) from t_user where user_name = 'admin' and user_password = ' ' or 1=1

动态关键字

  • if
<select id="findActiveBlogWithTitleLike"
    parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = 'ACTIVE'
    <if test="title != null">
        AND title like #{title}
    </if>
</select>

用户条件设置sql

  • choose,when,otherwise
int insertTest(@Param("a") String a, @Param("b") String b);

<select id="insertTest"  parameterType = "String" resultType="int">
    select count(1) from gtja_sys_maintain t
     <trim prefix="WHERE" prefixOverrides="AND | OR ">
        <choose>
            <when test="a == '1'.toString()">
             and t.id &gt; 368
            </when>
            <when test="b == '2'.toString()">
                and t.id &lt; 466 
            </when>       
        </choose>
        </trim>
    </select>

对于没匹配时,前置条件增加trim的prefix、prefixOverriders进行处理;
对于单独的参数或者String,直接使用@Param注解进行匹配

  • set
<update id="updateAuthorIfNecessary"
    parameterType="Author">
    update Author
    <trim prefix="where" prefixOverrides=",">
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email}</if>
    </set>
    where id=#{id}
    </trim>
</update>

对于更新操作,使用set,同时针对逗号做处理

  • foreach
    主要有 item,index,collection,open,separator,close;
    collection属性:1、如果传入的是单参数且参数类型是一个List的时候,collection属性值为list;2、否则是array
public class QueryVo(){
    private User user;
    private UserCustom userCustom;
    private List<integer> ids;
}

<select id="find" parameterType="qo" resultMap="userResult">
    select * from `user`
    <where>
        <foreach collection="ids" open=" and id in(" close=")" 
        item="id" separator=",">
            #{id}
        </foreach>
    </where>
    limit 0,10
</select>


<select id="selectByList" parameterType="java.util.List" resultType="user">
select * from user 
<where>
<!-- 传递List,List中是pojo -->
<if test="list!=null">
    <foreach collection="list" item="item" open="and id in("separator=","close=")">
          #{item.id} 
    </foreach>
</if>
</where>
</select>

<!-- 传递数组综合查询用户信息 -->
<select id="findByArray" parameterType="Object[]" resultType="user">
select * from user 
<where>
<!-- 传递数组 -->
<if test="array!=null">
<foreach collection="array" index="index" item="item" open="and id in("separator=","close=")">
            #{item.id} 
</foreach>
</if>
</where>
</select>
  • <sql id="queryCondition">将公共部分提取出来

mybatis缓存

分为一级缓存和二级缓存,通过缓存机制减轻数据压力,提高数据库性能。

  1. 一级缓存是SqlSession级别的缓存。默认开启
    如果SqlSession执行了DML操作(insert、update、delete),并执行commit()操作,mybatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象。
  2. 二级缓存时mapper级别的缓存,二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace

原理:

  • Cache的key由haskcode+checksum+查询参数+sqlmap Id+sql语句+调用方法名等构成。各元素不同时都会产生有不同的Key
  • 定时刷新或受条件触发刷新Key
  • 4种Cache实现,但是无法自定义扩展Cache
01c5e1a9.png
   <cacheModel id="maintainPojo-cache" type ="LRU" readOnly="true" serialize="false">  
       <flushInterval hours="24"/>
       <flushOnExecute statement="com.gtja.maintainManage.pojo.MaintainPojo.updateMaintainList"/>  
       <property value="500" name="size"/>  
    </cacheModel> 

属性:

  • id:是cachemodel的一个标识
  • readOnly:指缓存的数据对象是只读还是可读写,默认只读
  • serialize:只有在readOnly为false的情况下才生效;默认false=》所有session共享Cache,取出时实例是同一个。不序列化,外部更改有效(即取出的bean,被java后续更改后,cache的key仍然有效)
  • flushInterval:指定缓存自动刷新的时间,可以为hours,minutes,seconds,milliseconds
  • flushOnExecute:指定在发生哪些操作时,更新缓存。一般用于增删改时
  • type: 缓存的类型。目前使用的缓存类型有四种:
    1. FIFO:com.ibatis.sqlmap.engine.cache.fifo.FifoCacheController 用先进先出的原则来确定如何从Cache中清除对象
    2. LRU:com.ibatis.sqlmap.engine.cache.fifo.LruCacheController 用近期最少使用的原则来确定如何从Cache中清除对象
    3. MEMORY:com.ibatis.sqlmap.engine.cache.fifo.MemoryCacheController 默认,大多数情况下是最好的选择,能大大提高查用查询的性能,但对于当前不被使用的查询结果数据,将被清除以释放内存来分配其他对象
    4. OSCACHE:com.ibatis.sqlmap.engine.cache.fifo.OSCacheController

注意:对变更的数据,缓存时的实时性

常见问题:

  1. 操作clob字段时:
  • 原因:有可能出现在当从dual中取数据时,oracle会将clob对象的字段转为Long型
  • 解决:参数,指定jdbcType=CLOB 类型;增加begin 和end;不能用到select from dual 的操作(merge不行)
  1. mybatis中使用merge时使用update关键词,而ibatis则使用statement关键词

  2. ibatis中map作为参数时报错

  • 原因:Map作为resultClass时,必须指定具体的实现类,比如java.util.HashMap,否则会报错;java.util.Map是接口
  • 解决:resultClass="java.util.HashMap"
  1. mybatis判断条件等于具体值时,加上.toString()
<if test="newsImage != null and newsImage == '1'.toString()">  
    <![CDATA[ and len(newsImage) > 0 ]]>  
</if>
  1. mybatis识别不了单个入参
  • 原因:默认采用ONGL解析参数,所以会自动采用对象树的形式取Integer.cmpid,但基本类型没有这个属性
  • 解决:1:参数名全部替换为_parameter;2:Campusinfo sel_campusinfo(@Param(value="cmpid") int cmpid);
  1. mybatis批量增删
  1. mybatis调用存储过程
  1. ibatis和mybatis的区别:
  • iBatis 中namespace 不是必需的,且它的存在没有实际的意义。在 MyBatis 中,namespace 终于派上用场了,它使得映射文件与接口绑定变得非常自然
  • ibatis使用resultClass,mybatis使用resultType
  1. mybatis 插入空值時需要指定jdbcType
<insert id="save" parameterType="Province">
<![CDATA[
insert into t_yp_province
(fid,fname,fnumber,fsimpleName,fdescription,fcreateTime,flastUpdateTime,fdirect)
values
( #{id,jdbcType=VARCHAR},
#{name,jdbcType=VARCHAR},
#{number,jdbcType=VARCHAR},
#{simpleName,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR},
#{createTime,jdbcType=DATE},
#{lastUpdateTime,jdbcType=DATE},
#{direct,jdbcType=NUMERIC}
)
]]>
</insert>;

  1. mybatis3.2版本在web容器启动时出现假死且不报错:
    解决:升级mybatis到3.4版本

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

推荐阅读更多精彩内容

  • 湘中自古多才俊, 风流荟萃成古今。 时有乡间牧牛郎, 仿效先贤立典词。 胸无点墨学作文, 秉笔粗略贻大方。 传道何...
    湘江隐士阅读 446评论 0 0
  • 01习惯的养成 习惯的养成需要21天(很多公号文章是这样说的),但是这本书的作者说大多数习惯的改变需要18个月,我...
    猫良猫良阅读 160评论 0 1
  • 在人类的感情生活中,总是需要一个被倾诉、被需要的对象, 这个对象曾经使我们以为是唯一的,结果却不是。我们以为我们只...
    心时代第一执行官唐尧阅读 233评论 0 0