Mybatis

2018-09-15  本文已影响0人  攻城狮0902

Mybatis

概念

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

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


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

dataSource

元素

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

注:


动态SQL

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

sql注入:

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

动态关键字

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

用户条件设置sql

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注解进行匹配

<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,同时针对逗号做处理

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>

mybatis缓存

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

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

原理:

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> 

属性:

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

常见问题:

  1. 操作clob字段时:
  1. mybatis中使用merge时使用update关键词,而ibatis则使用statement关键词

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

  1. mybatis判断条件等于具体值时,加上.toString()
<if test="newsImage != null and newsImage == '1'.toString()">  
    <![CDATA[ and len(newsImage) > 0 ]]>  
</if>
  1. mybatis识别不了单个入参
  1. mybatis批量增删
  1. mybatis调用存储过程
  1. ibatis和mybatis的区别:
  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版本

上一篇下一篇

猜你喜欢

热点阅读