MyBatis使用與分析

MyBatis使用與分析

MyBatis的使用html

1:MyBatis初始化對象 configuration

configuration全局性配置java

<!-- 獨立使用MyBatis配置 -->
<configuration>
  <!-- 支持多套環境配置 -->
  <environments default="development">
    <environment id="development">
    <!-- 兩種事務管理類型:JDBC(手動),MANAGED(Spring或JAVAEE服務器託管) -->
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    <environment id="development2">
      ……
    </environment>
  </environments>
 
 
  <settings cacheModelsEnabled="true" lazyLoadingEnabled="true" enhancementEnabled="true" 
        errorTracingEnabled="true" maxSessions="1024" maxTransactions="512" maxRequests="2048" 
        useStatementNamespaces="true" />
 
  <!-- 維護映射文件 -->
  <mappers>
      <package name="org.xiuyuan.mybatis.demo.mapper"/>
  </mappers>
</configuration>
 
<!-- 與Spring結合配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 配置掃描Domain的包路徑 -->
    <property name="typeAliasesPackage" value="org.xiuyuan.mybatis.demo.domain"/>
    <!-- 配置掃描Mapper XML的位置 -->
    <property name="mapperLocations" value="classpath*:org/xiuyuan/mybatis/demo/mapper/*.xml"/>
    <!-- 配置mybatis配置文件的位置 -->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!-- 註冊類型轉換器 -->
    <property name="typeHandlers">
        <list>
            <ref bean="dateIntTypeHandler"></ref>
        </list>
    </property>
</bean>

 

2: StatementMapper配置與使用

MyBatis 真正的着力點是在映射語句中,與JDBC相比將節省95%的代碼量 ,對於使用StatemetMapper接口的DAO層實現,比hibernate代碼還要少.git

(1):SQL 映射文件結構:

cache - 配置給定命名空間的緩存。 
cache-ref – 從其餘命名空間引用緩存配置。 
resultMap – 最複雜標籤,用來描述如何從數據庫結果集中來加載對象。
sql – 能夠重用的 SQL 塊,也能夠被其餘語句引用。 
insert – 映射插入語句 
update – 映射更新語句 
delete – 映射刪除語句 
select – 映射查詢語句github

MyBatis映射SQL的3種方式

 

//第一種使用xml和徹底限定名調用映射
//這裏注意當心namespace ,parameterType ,resultType這幾項配置
//sql列別名對應pojo屬性名,自動映射
<mapper namespace="org.xiuyuan.mybatis.demo.mapper.BranchStatusMapper">
    <select id="getBranchStatusListById" parameterType="int" resultType="Blog">
        select
            branch_id "branchId",is_signed "isSigned",is_online "isOnline",type,
            cooperate_times "cooperateTimes",created_time "createdTime",last_modified "lastModified"
        from branch_status where branch_id = #{id}
    </select>
</mapper>
BranchStatusMapper branchStatus = (branchStatus) session.selectOne(this.getClass.getName()+".getBranchStatusListById", 101);
 
 
//第二種採用接口調用註解映射語句
public interface BranchStatusMapper {
 
    @SelectProvider(type = BrnchStatusSqlProvider.class, method = "getBrnchStatusById")  
    //或者直接寫sql
    @Select("select branch_id "branchId",is_signed "isSigned",cooperate_times "cooperateTimes" from branch_status where branch_id = #{id}")
    @Options(useCache = true, flushCache = false, timeout = 10000)
    List<BranchStatus> getBranchStatusListById(Integer id);
 
} 
 
//第三種採用SQL提供類
public class BranchStatusSqlProvider {
    public String getBrnchStatusById(Map<String, Object> parameters) {
        BEGIN();
        SELECT("branch_id "branchId",is_signed "isSigned",is_online "isOnline",type,cooperate_times "cooperateTimes",created_time "createdTime",last_modified "lastModified"");
        FROM("branch_status");
        WHERE("branch_id = #{id}");
        return SQL();
    }
}
BranchStatusMapper mapper = session.getMapper(BranchStatusMapper.class);
BranchStatus branchStatus = mapper.getBranchStatusListById(101);
 
//第三種採用接口調用xml映射語句(推薦這種方式,須要spring-mybatis)
<?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">
<mapper namespace="com.sankuai.meituan.crm.dao.mapper.BranchStatusMapper">
    <sql id="branch_status_column_property">
        <![CDATA[
          branch_id "branchId",is_signed "isSigned",is_online "isOnline",type,cooperate_times "cooperateTimes",created_time "createdTime",last_modified "lastModified"
        ]]>
    </sql>
</mapper>
// 接口定義
public interface BranchStatusMapper {
  
    List<BranchStatus> getBranchStatusListById(Integer id);
 
} 
<!-- 配置掃描Mapper接口的包路徑 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.sankuai.meituan.crm.dao.mapper"/>
</bean>
//可直接在service注入,mybatis動態生成接口實現類
@Resource
private BranchStatusMapper branchStatusMapper;

 

XML映射細節算法

我推薦使用xml的方式,註解方式不便於SQL語句的修改和優化spring

Insert映射
//SQL片斷 (抽取出公共部分供多出調用)
<sql id="partner_table_columns">
        <![CDATA[
            name, city_id, district_id, qualification_id, qualification_code, qualification_type, creator_id, create_time, modifier_id, modify_time, status, integrity, expire_date, lock_version
        ]]>
</sql>
<sql id="partner_java_property">
    <![CDATA[
        #{name}, #{cityId}, #{districtId}, #{qualificationId}, #{qualificationCode}, #{qualificationType}, #{creatorId}, #{createTime}, #{modifierId}, #{modifyTime}, #{status}, #{integrity}, #{expireDate}, #{lockVersion}
        ]]>
</sql>
 
 
//主鍵策略:
//若是使用的數據庫支持自動生成主鍵,那麼就能夠設置 useGeneratedKeys= 」true」 ,把keyProperty 設成對應的列。
<insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="Partner">
    insert into
        partner(<include refid="partner_table_columns"/> )
    values
        (<include refid="partner_java_property"/>)
</insert>
Update和delete映射
<update id="update" parameterType="BranchStatus">
    update branch_status
    <set>
        <if test="isSigned != null">is_signed=#{isSigned},</if>
        <if test="isOnline != null">is_online=#{isOnline},</if>
        <if test="type != null">type=#{type},</if>
        <if test="cooperateTimes != null">cooperate_times=#{cooperateTimes},</if>
        last_modified = unix_timestamp()
    </set>
    where
    branch_id = #{branchId}
</update>
 
<delete id="deleteBranchStatus」 parameterType="int">
    delete from branch_user where branch_id = #{id}
</delete>

 

 

Select和動態sql
查詢映射是使用 MyBatis 時最經常使用的元素,也是最複雜的映射
動態 SQL標籤
if 
choose(when,otherwise)  
trim(where,set) 
foreach 
<!-- 動態映射 -->
<!-- if, where ,foreach標籤,簡化動態sql和格式控制成本 -->
<select id="getBranchIdsByParams" parameterType="map" resultType="int">
    select
      bs.branch_id
    from branch_status bs
    <if test="(statusList != null and statusList.size > 0) or expireTime !=null">
    inner join branch_user bu on  bu.branch_id = bs.branch_id
    </if>
    <where>
        <if test="isSigned != null">
            bs.is_signed = #{isSigned}
        </if>
        <if test="isOnline != null">
            and bs.is_online = #{isOnline}
        </if>
        <if test="statusList != null and statusList.size > 0">
             and bu.status in
            <foreach collection="statusList" index="index" item="status" open="(" separator="," close=")">
                #{status}
            </foreach>
        </if>
        <if test="expireTime != null">
            <![CDATA[
            and bu.expire_time > 0 and bu.expire_time < #{expireTime}
            ]]>
        </if>
    </where>
</select>

ResoutMap映射,處理一對一,一對多,多對多等複雜狀況,sql

不建議大範圍使用,建議把鏈接拆分大聯接數據庫

<!-- 注意resultMap的命名 -->
<select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>
<!-- resultMap映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType=" Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType=" Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

MyBatis關鍵組件分析

1:MyBatis關鍵類結構層次圖

alt

2:根據最簡實例分析MyBatis源碼:

準備工做緩存

Mybatis完成SQL查詢須要的最簡代碼以下:安全

String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
try {
    BranchUser user = (BranchUser) session.selectOne("BranchUserDao.getBranchUserById", "1");
    System.out.println(user);
} finally {
    session.close();
}
 打開一個session, 分析內部操做
SqlSession session = ssf.openSession();
//DefaultSqlSessionFactory的 openSession()方法內容以下:
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
 
//openSessionFromDataSource內部
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //獲取加載配置文件的環境信息
      final Environment environment = configuration.getEnvironment();
      //設置鏈接的事務信息(是否自動提交、事務級別),從配置環境中獲取事務工廠,事務工廠獲取一個新的事務。
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //傳入事務對象獲取一個新的執行器,並傳入執行器、配置信息等獲取一個執行會話對象。
      final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
      return new DefaultSqlSession(configuration, executor);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}
// 在openSessionFromDataSource代碼中重點在newExecutor和DefaultSqlSession   
//newExecutor到底作了什麼?
public Executor newExecutor (Transaction transaction, ExecutorType executorType){
    //判斷執行器類型,若是配置文件中沒有配置執行器類型,則採用默認執行類型ExecutorType.SIMPLE。
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
 // 根據執行器類型返回不一樣類型的執行器(執行器有三種,分別是 BatchExecutor、SimpleExecutor和CachingExecutor)
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    //執行器綁定攔截器插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}
//DefaultSqlSession 是作什麼的?
//DefaultSqlSession實現了SqlSession接口,裏面有各類各樣的SQL執行方法,主要用於SQL操做的對外接口,它會的調用執行器來執行實際的SQL語句。   
 
session.selectOne("BranchUserDao.getBranchUserById", "1");
 
//selectOne方法實現
public Object selectOne (String statement, Object parameter){
  // Popular vote was to return null on 0 results and throw exception on too many.
  List list = selectList(statement, parameter);
  if (list.size() == 1) {
      return list.get(0);
  } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
      return null;
  }
}
//本質上都是調用selectList實現
public List selectList (String statement, Object parameter){
    return selectList(statement, parameter, RowBounds.DEFAULT);
}
 
public List selectList (String statement, Object parameter, RowBounds rowBounds){
    try {
        //根據SQL的ID到配置信息中找對應的MappedStatement,初始化時MyBatis會將SQL塊解析並放入Map<String, MappedStatement> mappedStatements 中
        //並將MappedStatement對象放到一個Map裏面進行存放,Map的key值是該SQL塊的ID。
        MappedStatement ms = configuration.getMappedStatement(statement);
        //調用執行器的query方法,傳入MappedStatement對象、SQL參數對象、範圍對象和結果處理方式。
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}
//執行器(SimpleExecutor)執行sql代碼分析
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                            ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    //獲取配置信息對象。
    Configuration configuration = ms.getConfiguration();
    //經過配置對象獲取一個新的StatementHandler,生成結果處理對象(見下文)。
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
    //預處理StatementHandler對象,獲得Statement對象。
    stmt = prepareStatement(handler, ms.getStatementLog());
    //傳入Statement和結果處理對象,經過StatementHandler的query方法來執行SQL,並對執行結果進行處理。
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}
 
//newStatementHandler方法分析
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, 
                                            RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //根據相關的參數獲取對應的StatementHandler對象。
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //爲StatementHandler對象綁定攔截器插件。
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}
//RoutingStatementHandler構造方法分析
//根據 MappedStatement對象的StatementType來建立不一樣的StatementHandler,這個跟前面執行器代碼相似
//StatementType有STATEMENT、PREPARED和CALLABLE三種類型,跟JDBC的Statement類型對應
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }
}
//最後執行prepareStatement方法,執行SQL獲取結果.
private Statement prepareStatement (StatementHandler handler)throws SQLException {
    Statement stmt;
    Connection connection = transaction.getConnection();
    //從鏈接中獲取Statement對象
    stmt = handler.prepare(connection);
    //處理預編譯的傳入參數
    handler.parameterize(stmt);
    return stmt;
}

 

MyBatis分析之緩存

  • 1 理解MyBatis緩存
MyBatis和Hibernte同樣,都提供提供了一級緩存和二級緩存

一級緩存基於 PerpetualCache 的 HashMap 本地緩存,其存儲做用域爲 Session,當 Session flush 或 close 以後,該Session中的全部 Cache 就將清空。   

二級緩存與一級緩存其機制相同,默認也是採用 PerpetualCache,HashMap存儲,可是它的存儲做用域爲 Mapper(Namespace),而且可自定義存儲源,如 Ehcache、Memcached等。

對於緩存數據更新機制,當某一個做用域(一級緩存Session/二級緩存Namespaces)的進行了 insert/update/delete 操做後,默認該做用域下全部 select 中的緩存將被clear。

MyBatis 的緩存採用了delegate機制及裝飾器模式設計,當put、get、remove時,其中會通過多層 delegate cache 處理,其Cache類別有:BaseCache(基礎緩存)、EvictionCache(排除算法緩存) 、DecoratorCache(裝飾器緩存):

 

MyBatis 對於其 緩存Key 的生成採起規則爲:[hashcode : checksum : mappedStementId : offset : limit : executeSql : queryParams] 見代碼:BaseExecutor.createCacheKey

myBatis對Cache的處理流程:

alt

Executor執行器接口類結構圖
alt
BaseExecutor 執行器抽象類。實現一些通用方法,如createCacheKey 之類。採用 模板模式 將具體的數據庫操做邏輯(doUpdate、doQuery)交由子類實現。類內部變量PerpetualCache localCache;在該類採用 PerpetualCache 實現基於 Map 存儲的一級緩存,其 query 方法以下:
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    // 執行器已關閉
    if (closed) throw new ExecutorException("Executor was closed.");
    List list;
    try {
      queryStack++;
      // 建立緩存Key
      CacheKey key = createCacheKey(ms, parameter, rowBounds);
      // 從本地緩存在中獲取該 key 所對應的結果集
      final List cachedList = (List) localCache.getObject(key);
      // 在緩存中找到數據
      if (cachedList != null) {
        list = cachedList;
      } else { // 未從本地緩存中找到數據,開始調用數據庫查詢
        //爲該 key 添加一個佔位標記
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          // 執行子類所實現的數據庫查詢 操做
          list = doQuery(ms, parameter, rowBounds, resultHandler);
        } finally {
          // 刪除該 key 的佔位標記
          localCache.removeObject(key);
        }
        // 將db中的數據添加至本地緩存中
        localCache.putObject(key, list);
      }
    } finally {
      queryStack--;
    }
    // 刷新當前隊列中的全部 DeferredLoad實例,更新 MateObject
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
    }
    return list;
  }

 

CachingExecutor 二級緩存執行器。使用 delegate機制。其委託執行的類是 BaseExcutor。 當沒法從二級緩存獲取數據時,須要經過BaseExcutor中進行查詢,再緩存.流程圖爲:
alt
程爲: 從二級緩存中進行查詢 -> 若是緩存中沒有,交給給 BaseExecutor -> 進入一級緩存中查詢 -> 若是也沒有 -> 則執行 JDBC 查詢,代碼以下:
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    if (ms != null) {
      // 獲取二級緩存實例
      Cache cache = ms.getCache();
      if (cache != null) {
        flushCacheIfRequired(ms);
        // 獲取 讀鎖( Read鎖可由多個Read線程同時保持)
        cache.getReadWriteLock().readLock().lock();
        try {
          // 當前 Statement 是否啓用了二級緩存
          if (ms.isUseCache()) {
            // 將建立 cache key 委託給 BaseExecutor 建立
            CacheKey key = createCacheKey(ms, parameterObject, rowBounds);
            final List cachedList = (List) cache.getObject(key);
            // 從二級緩存中找到緩存數據
            if (cachedList != null) {
              return cachedList;
            } else {
              // 未找到緩存,很委託給 BaseExecutor 執行查詢
              List list = delegate.query(ms, parameterObject, rowBounds, resultHandler);
              tcm.putObject(cache, key, list);
              return list;
            }
          } else { // 沒有啓動用二級緩存,直接委託給 BaseExecutor 執行查詢
            return delegate.query(ms, parameterObject, rowBounds, resultHandler);
          }
        } finally {
          // 當前線程釋放 Read 鎖
          cache.getReadWriteLock().readLock().unlock();
        }
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler);
}

MyBatis小知識

1:編碼的方式提取SQL語句

Configuration configuration = sqlSession.getConfiguration();
MappedStatement ms = configuration.getMappedStatement(sqlStatementId);
BoundSql boundSql = ms.getBoundSql(param);
String sql = boundSql.getSql(); 

2:實現自定義類型轉換(int 轉Date)

//1:實現 TypeHandler轉換接口
/**
 * @author zhangyijun
 * java中的Date和jdbc中的int轉換
 */
//註解生命須要轉換的類型
@MappedTypes(value = Date.class)
@MappedJdbcTypes(value = JdbcType.NUMERIC)
public class DateIntTypeHandler implements TypeHandler<Date> {
    @Override
    public Date getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String value = cs.getString(columnIndex);
        if (StringUtil.isBlank(value)) {
            Integer time = Integer.valueOf(value);
            return DateUtil.fromUnixTime(time);
        }
        return null;
    }
    @Override
    public void setParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        if (parameter != null) {
            Integer second = DateUtil.date2Unixtime(parameter);
            ps.setInt(i, second);
        }
    }
    @Override
    public Date getResult(ResultSet rs, String columnName) throws SQLException {
        String value = rs.getString(columnName);
        if (StringUtil.isBlank(value)) {
            Integer time = Integer.valueOf(value);
            return DateUtil.fromUnixTime(time);
        }
        return null;
    }
    @Override
    public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
        String value = rs.getString(columnIndex);
        if (StringUtil.isBlank(value)) {
            Integer time = Integer.valueOf(value);
            return DateUtil.fromUnixTime(time);
        }
        return null;
    }
}
//2:註冊類型轉換器
<!-- 類型轉換器 -->
<bean id="dateIntTypeHandler" class="org.xiuyuan.mybatis.demo.dao.handler.DateIntTypeHandler"></bean>
<!-- 註冊類型轉換器 -->
<property name="typeHandlers">
    <list>
        <ref bean="dateIntTypeHandler"></ref>
        </list>
</property>
//3:聲明須要類型轉換的字段
#{createdTime,javaType=Date,jdbcType=NUMERIC}

 

3:經過Mysql數據字典生成sql_xml片斷,防止寫錯
-- 查詢branch_status全部列
select
    group_concat(column_name)
from
    information_schema.columns
where
    table_schema = 'you_database' and table_name = 'you_table'
 
-- 生成列對應的java屬性
select
    group_concat(case when locate('_',column_name)>0 then concat(replace(column_name , concat('_',@shortChar:=substring(column_name,locate('_',column_name)+1,1)),upper(@shortChar))) else column_name end)
from
    information_schema.columns
where
    table_schema = 'you_database' and table_name = 'branch_status'
district_id "districtId" -- 生成列+別名(屬性名 如:is_online "isOnline") select group_concat(case when locate('_',column_name)>0 then concat(column_name,' "',replace(column_name , concat('_',@shortChar:=substring(column_name,locate('_',column_name)+1,1)),upper(@shortChar)),'"') else column_name end) from information_schema.columns where table_schema = 'you_database' and table_name = 'branch_status'
-- 生成直接提取ognl表達式(如:#{branchId}) select group_concat(concat('#{',replace(column_name , concat('_',@shortChar:=substring(column_name,locate('_',column_name)+1,1)), upper(@shortChar) ),'}')) from information_schema.columns where table_schema = 'you_database' and table_name = 'branch_status'
-- 生成加入用於判空表達式,用於update(如:<if test = "name != null">name=#{name},</if>) select group_concat(concat('<if test = "' , @java_column_name:=replace(column_name , concat('_',@shortChar:=substring(column_name,locate('_',column_name)+1,1)), upper(@shortChar) ) , ' != null">', column_name , '=#{',@java_column_name,'},</if>') SEPARATOR '') from information_schema.columns where table_schema = 'you_database' and table_name = 'branch_status'
<if test = "name != null"> and name=#{name}</if> -- 生成加入用於判空表達式,用於where條件(如:<if test = "isSigned != null"> and is_signed=#{isSigned},) select group_concat(concat('<if test = "' , @java_column_name:=replace(column_name , concat('_',@shortChar:=substring(column_name,locate('_',column_name)+1,1)), upper(@shortChar) ) , ' != null"> and ', column_name , '=#{', @java_column_name,'},</if>') SEPARATOR '') from information_schema.columns where table_schema = 'you_database' and table_name = 'branch_status'

3: 使用autoMappingBehavior參數基於OGNL映射字段:

autoMappingBehavior屬性官方解釋:
    MyBatis 自動映射列到字段/ 屬性方式。PARTIAL 只會自動映射簡單, 沒有嵌套的結果。FULL 會自動映射任意複雜的結果(嵌套的或其餘狀況)
-- 配置全局屬性 autoMappingBehavior=FULL
-- 當心性能開銷 TODO 測試
select u.branch_id "branchId" , s.type "branchStatus.type" ...
from branch_user u, branc_status s
where s.branch_id = u.branch_id

4:使用mapUnderscoreToCamelCase完成下劃線命名到駝峯命名

-- mapUnderscoreToCamelCase =true //默認false
-- 自動映射,無需再經過列別名指定屬性名
select branch_id,is_signed,is_online,type,cooperate_times,created_time,last_modified
from branch_status
where branch_id = #{id}

5: sql映射中的 # , $ 區分

使用#{}格式 MyBatis 建立預處理語句屬設置安全的值(?)  。

使用${}格式 直接在 SQL 語 句中插入一個不改變的字符串,有sql注入風險。

相關文章
相關標籤/搜索