Mybatis

1.Mybatis的架構

1.1 Mybatis的框架分層

這裏寫圖片描述

1.2 MyBatis的實現原理

mybatis底層仍是採用原生jdbc來對數據庫進行操做的,只是經過 SqlSessionFactory,SqlSession Executor,StatementHandler,ParameterHandler,ResultHandler和TypeHandler等幾個處理器封裝了這些過程php

執行器:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 參數處理器: ParameterHandler (getParameterObject, setParameters) 結構處理器 ResultSetHandler (handleResultSets, handleOutputParameters) sql查詢處理器:StatementHandler (prepare, parameterize, batch, update, query) 

其中StatementHandler用經過ParameterHandler與ResultHandler分別進行參數預編譯 與結果處理。而ParameterHandler與ResultHandler都使用TypeHandler進行映射。以下圖: 
這裏寫圖片描述java

2.Mybatis工做過程

經過讀mybatis的源碼進行分析mybatis的執行操做的整個過程,咱們經過debug調試就能夠知道Mybatis每一步作了什麼事,我先把debug每一步結果 截圖,而後在分析這個流程。 
第一步:讀取配置文件,造成InputStreammysql

2.1 建立SqlSessionFacotry的過程

這裏寫圖片描述 
從debug調試看出 返回的 sqlSessionFactory 是DefaultSesssionFactory類型的,可是configuration此時已經被初始化了。查看源碼後畫以下建立DefaultSessionFactory的時序圖: 
這裏寫圖片描述程序員

2.2 建立SqlSession的過程

這裏寫圖片描述 
從debug調試 看出SqlSessinoFactory.openSession() 返回的sqlSession是 DefaultSession類型的,此SqlSession裏包含一個Configuration的對象,和一個Executor對象。查看源碼後畫以下建立DefaultSession的時序圖:算法

這裏寫圖片描述

2.3 建立Mapper的過程

這裏寫圖片描述 
從debug調試能夠看出,mapper是一個Mapper代理對象,並且初始化了Configuration對象,Executor的對象。查看源碼後畫以下建立Mapper的時序圖: 
這裏寫圖片描述sql

2.4 執行CRUD過程

2.4.1 以select爲例查看各步執行的源碼

1.mapper.selectEmployeeList()實際上是MapperProxy執行invoke方法,此方法顯示是判斷Method的方法是否是Object的toString等方法若是不是就執行MapperMethod數據庫

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 判斷Method的方法是否是Object的toString等方法 if(Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } } else { //判斷private final Map<Method, MapperMethod> methodCache;這個map裏面有沒有這個方法的一級緩存,若是沒 MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); } } //查詢一級緩存和設置一級緩存 private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method); if(mapperMethod == null) { mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()); this.methodCache.put(method, mapperMethod); } return mapperMethod; }

通過上面的調用後進入MapperMethod裏面執行apache

//判斷sql命令類型
public Object execute(SqlSession sqlSession, Object[] args) {
        Object param; Object result; if(SqlCommandType.INSERT == this.command.getType()) { param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); } else if(SqlCommandType.UPDATE == this.command.getType()) { param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); } else if(SqlCommandType.DELETE == this.command.getType()) { param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); } else if(SqlCommandType.SELECT == this.command.getType()) { //咱們測試的是select類型,則再判斷這個方法的返回類型 if(this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if(this.method.returnsMany()) { //咱們是查詢列表,此方法執行 result = this.executeForMany(sqlSession, args); } else if(this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); } } else { if(SqlCommandType.FLUSH != this.command.getType()) { throw new BindingException("Unknown execution method for: " + this.command.getName()); } result = sqlSession.flushStatements(); } if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } } private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { //將param作處理 自動處理爲param1,param2.. Object param = this.method.convertArgsToSqlCommandParam(args); List result; if(this.method.hasRowBounds()) { RowBounds rowBounds = this.method.extractRowBounds(args); //調用該對象的DefaultSqlSession的selectList方法 result = sqlSession.selectList(this.command.getName(), param, rowBounds); } else { result = sqlSession.selectList(this.command.getName(), param); } return !this.method.getReturnType().isAssignableFrom(result.getClass())?(this.method.getReturnType().isArray()?this.convertToArray(result):this.convertToDeclaredCollection(sqlSession.getConfiguration(), result)):result; } //處理參數方法 public Object convertArgsToSqlCommandParam(Object[] args) { int paramCount = this.params.size(); if(args != null && paramCount != 0) { if(!this.hasNamedParameters && paramCount == 1) { return args[((Integer)this.params.keySet().iterator().next()).intValue()]; } else { Map<String, Object> param = new MapperMethod.ParamMap(); int i = 0; for(Iterator i$ = this.params.entrySet().iterator(); i$.hasNext(); ++i) { Entry<Integer, String> entry = (Entry)i$.next(); param.put(entry.getValue(), args[((Integer)entry.getKey()).intValue()]); String genericParamName = "param" + String.valueOf(i + 1); if(!param.containsKey(genericParamName)) { param.put(genericParamName, args[((Integer)entry.getKey()).intValue()]); } } return param; } } else { return null; } } 

 

調用DefaultSqlSession的selectList的方法api

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { List var5; try { //獲取MappedStatement對象 MappedStatement ms = this.configuration.getMappedStatement(statement); //調用cachingExecutor執行器的方法 var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } return var5; } //CachingExector的query方法 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql); //調用下2代碼 return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } //2代碼 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if(cache != null) { this.flushCacheIfRequired(ms); if(ms.isUseCache() && resultHandler == null) { this.ensureNoOutParams(ms, parameterObject, boundSql); List<E> list = (List)this.tcm.getObject(cache, key); if(list == null) { //這裏是調用Executor裏的query方法 若是開啓了緩存這掉CachingExecutor的 若是沒有則是調用BaseExecutor的 list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); this.tcm.putObject(cache, key, list); } return list; } } return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

 

BaseExecutor的query方法緩存

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if(this.closed) { throw new ExecutorException("Executor was closed."); } else { if(this.queryStack == 0 && ms.isFlushCacheRequired()) { this.clearLocalCache(); } List list; try { ++this.queryStack; list = resultHandler == null?(List)this.localCache.getObject(key):null; if(list != null) { this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //若是緩存中沒有就從數據庫中查詢 list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { --this.queryStack; } if(this.queryStack == 0) { Iterator i$ = this.deferredLoads.iterator(); while(i$.hasNext()) { BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next(); deferredLoad.load(); } this.deferredLoads.clear(); if(this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { this.clearLocalCache(); } } return list; } } //從數據庫中查詢 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //放入緩存 this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER); List list; try { //此處是調用子Executor的方法,ExecutorType默認是使用的SimpleExecutor list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { this.localCache.removeObject(key); } this.localCache.putObject(key, list); if(ms.getStatementType() == StatementType.CALLABLE) { this.localOutputParameterCache.putObject(key, parameter); } return list; }

 

SimpleExecutor的doQuery方法

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; List var9; try { Configuration configuration = ms.getConfiguration(); //建立StateMentHandler處理器 StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //調用下3的方法 stmt = this.prepareStatement(handler, ms.getStatementLog()); //調用no4的方法 var9 = handler.query(stmt, resultHandler); } finally { this.closeStatement(stmt); } return var9; } //下3方法 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Connection connection = this.getConnection(statementLog); Statement stmt = handler.prepare(connection); //SatementHanlder 採用PreparedStatementHandler來實現此方法,而PreparedStatementHandler調用的是父接口ParameterHandler的方法 handler.parameterize(stmt); return stmt; }

 

ParameterHandler參數處理器的方法

public interface ParameterHandler { Object getParameterObject(); //此方法是用DefaultParameterHandler實現的 void setParameters(PreparedStatement var1) throws SQLException; }

 

DefaultParameterHandler默認參數處理器的方法

public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings(); if(parameterMappings != null) { for(int i = 0; i < parameterMappings.size(); ++i) { ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i); if(parameterMapping.getMode() != ParameterMode.OUT) { String propertyName = parameterMapping.getProperty(); Object value; if(this.boundSql.hasAdditionalParameter(propertyName)) { value = this.boundSql.getAdditionalParameter(propertyName); } else if(this.parameterObject == null) { value = null; } else if(this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) { value = this.parameterObject; } else { MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject); value = metaObject.getValue(propertyName); } //這裏用調用 TypeHandler類型映射處理器來映射 TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if(value == null && jdbcType == null) { jdbcType = this.configuration.getJdbcTypeForNull(); } try { //類型處理器設置參數映射 typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException var10) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10); } catch (SQLException var11) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11); } } } } }

no4的方法

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { //此處調用原生sql的處理器 PreparedStatement ps = (PreparedStatement)statement; //發出原生sql命令 ps.execute(); //採用ResultHandler結果處理器對結果集封裝 return this.resultSetHandler.handleResultSets(ps); }

ResultHandler代碼

public interface ResultSetHandler { //此處調用的是DefaultResultSetHandler的方法 <E> List<E> handleResultSets(Statement var1) throws SQLException; void handleOutputParameters(CallableStatement var1) throws SQLException; } 

DefaultResultSetHandler的方法

public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId()); List<Object> multipleResults = new ArrayList(); int resultSetCount = 0; ResultSetWrapper rsw = this.getFirstResultSet(stmt); List<ResultMap> resultMaps = this.mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); this.validateResultMapsCount(rsw, resultMapCount); while(rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount); this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null); rsw = this.getNextResultSet(stmt); this.cleanUpAfterHandlingResultSet(); ++resultSetCount; } String[] resultSets = this.mappedStatement.getResulSets(); if(resultSets != null) { while(rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]); if(parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId); this.handleResultSet(rsw, resultMap, (List)null, parentMapping); } rsw = this.getNextResultSet(stmt); this.cleanUpAfterHandlingResultSet(); ++resultSetCount; } } return this.collapseSingleResultList(multipleResults); } //處理結果集 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { if(parentMapping != null) { this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping); } else if(this.resultHandler == null) { DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory); this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null); multipleResults.add(defaultResultHandler.getResultList()); } else { this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null); } } finally { this.closeResultSet(rsw.getResultSet()); } } private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { if(resultMap.hasNestedResultMaps()) { this.ensureNoRowBounds(); this.checkResultHandler(); this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } } private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext(); this.skipRows(rsw.getResultSet(), rowBounds); Object rowValue = null; while(this.shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, (String)null); CacheKey rowKey = this.createRowKey(discriminatedResultMap, rsw, (String)null); Object partialObject = this.nestedResultObjects.get(rowKey); if(this.mappedStatement.isResultOrdered()) { if(partialObject == null && rowValue != null) { this.nestedResultObjects.clear(); this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } //獲取行的值 rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject); } else { rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject); if(partialObject == null) { this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } } } if(rowValue != null && this.mappedStatement.isResultOrdered() && this.shouldProcessMoreRows(resultContext, rowBounds)) { this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } } String resultMapId = resultMap.getId(); Object resultObject = partialObject; if(partialObject != null) { MetaObject metaObject = this.configuration.newMetaObject(partialObject); this.putAncestor(partialObject, resultMapId, columnPrefix); this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false); this.ancestorObjects.remove(resultMapId); } else { ResultLoaderMap lazyLoader = new ResultLoaderMap(); resultObject = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if(resultObject != null && !this.typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { MetaObject metaObject = this.configuration.newMetaObject(resultObject); boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty(); if(this.shouldApplyAutomaticMappings(resultMap, true)) { foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; this.putAncestor(resultObject, resultMapId, columnPrefix); foundValues = this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues; this.ancestorObjects.remove(resultMapId); foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues?resultObject:null; } if(combinedKey != CacheKey.NULL_CACHE_KEY) { this.nestedResultObjects.put(combinedKey, resultObject); } } return resultObject; } private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) { return resultMap.getAutoMapping() != null?resultMap.getAutoMapping().booleanValue():(isNested?AutoMappingBehavior.FULL == this.configuration.getAutoMappingBehavior():AutoMappingBehavior.NONE != this.configuration.getAutoMappingBehavior()); } private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List<DefaultResultSetHandler.UnMappedColumAutoMapping> autoMapping = this.createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if(autoMapping.size() > 0) { Iterator i$ = autoMapping.iterator(); while(true) { //這裏使用了內部類對參數和結果集進行映射 DefaultResultSetHandler.UnMappedColumAutoMapping mapping; Object value; do { if(!i$.hasNext()) { return foundValues; } mapping = (DefaultResultSetHandler.UnMappedColumAutoMapping)i$.next(); value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); } while(value == null && !this.configuration.isCallSettersOnNulls()); if(value != null || !mapping.primitive) { metaObject.setValue(mapping.property, value); } foundValues = true; } } else { return foundValues; } } private static class UnMappedColumAutoMapping { private final String column; private final String property; private final TypeHandler<?> typeHandler; private final boolean primitive; //此處才類型器對結果進行映射 public UnMappedColumAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) { this.column = column; this.property = property; this.typeHandler = typeHandler; this.primitive = primitive; } 

一、#{}和${}的區別是什麼?

#{}是預編譯處理,${}是字符串替換。
Mybatis在處理#{}時,會將sql中的#{}替換爲?號,調用PreparedStatement的set方法來賦值;
Mybatis在處理${}時,就是把${}替換成變量的值。
使用#{}能夠有效的防止SQL注入,提升系統安全性。

二、當實體類中的屬性名和表中的字段名不同 ,怎麼辦 ?

第1種: 經過在查詢的sql語句中定義字段名的別名,讓字段名的別名和實體類的屬性名一致
<select id=」selectorder」 parametertype=」int」 resultetype=」me.gacl.domain.order」> select order_id id, order_no orderno ,order_price price form orders where order_id=#{id}; </select> 
第2種: 經過<resultMap>來映射字段名和實體類屬性名的一一對應的關係
<select id="getOrder" parameterType="int" resultMap="orderresultmap"> select * from orders where order_id=#{id} </select> <resultMap type=」me.gacl.domain.order」 id=」orderresultmap」> <!–用id屬性來映射主鍵字段–> <id property=」id」 column=」order_id」> <!–用result屬性來映射非主鍵字段,property爲實體類屬性名,column爲數據表中的屬性–> <result property = 「orderno」 column =」order_no」/> <result property=」price」 column=」order_price」 /> </reslutMap>

三、 模糊查詢like語句該怎麼寫?

第1種:在Java代碼中添加sql通配符。
string wildcardname = 「%smi%」; list<name> names = mapper.selectlike(wildcardname); <select id=」selectlike」> select * from foo where bar like #{value} </select>
第2種:在sql語句中拼接通配符,會引發sql注入
string wildcardname = 「smi」; list<name> names = mapper.selectlike(wildcardname); <select id=」selectlike」> select * from foo where bar like "%"#{value}"%" </select>

四、一般一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工做原理是什麼?Dao接口裏的方法,參數不一樣時,方法能重載嗎?

Dao接口,就是人們常說的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法內的參數,就是傳遞給sql的參數。Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串做爲key值,可惟必定位一個MappedStatement,舉例:com.mybatis3.mappers.StudentDao.findStudentById,能夠惟一找到namespace爲com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每個<select>、<insert>、<update>、<delete>標籤,都會被解析爲一個MappedStatement對象。

Dao接口裏的方法,是不能重載的,由於是全限名+方法名的保存和尋找策略。

Dao接口的工做原理是JDK動態代理,Mybatis運行時會使用JDK動態代理爲Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所表明的sql,而後將sql執行結果返回。

五、Mybatis是如何進行分頁的?分頁插件的原理是什麼?

Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁,能夠在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可使用分頁插件來完成物理分頁。

分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,而後重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。

六、Mybatis是如何將sql執行結果封裝爲目標對象並返回的?都有哪些映射形式?

答:第一種是使用<resultMap>標籤,逐必定義列名和對象屬性名之間的映射關係。第二種是使用sql列的別名功能,將列別名書寫爲對象屬性名,好比T_NAME AS NAME,對象屬性名通常是name,小寫,可是列名不區分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至能夠寫成T_NAME AS NaMe,Mybatis同樣能夠正常工做。

有了列名與屬性名的映射關係後,Mybatis經過反射建立對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是沒法完成賦值的。

七、如何執行批量插入?

首先,建立一個簡單的insert語句:
<insert id=」insertname」> insert into names (name) values (#{value}) </insert>
而後在java代碼中像下面這樣執行批處理插入:
list<string> names = new arraylist(); names.add(「fred」); names.add(「barney」); names.add(「betty」); names.add(「wilma」); // 注意這裏 executortype.batch sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch); try { namemapper mapper = sqlsession.getmapper(namemapper.class); for (string name : names) { mapper.insertname(name); } sqlsession.commit(); } finally { sqlsession.close(); }

八、如何獲取自動生成的(主)鍵值?

insert 方法老是返回一個int值 - 這個值表明的是插入的行數。 
而自動生成的鍵值在 insert 方法執行完後能夠被設置到傳入的參數對象中。 
示例:
<insert id=」insertname」 usegeneratedkeys=」true」 keyproperty=」id」> insert into names (name) values (#{name}) </insert> name name = new name(); name.setname(「fred」); int rows = mapper.insertname(name); // 完成後,id已經被設置到對象中 system.out.println(「rows inserted = 」 + rows); system.out.println(「generated key value = 」 + name.getid());

九、在mapper中如何傳遞多個參數?

第1種:
//DAO層的函數 Public UserselectUser(String name,String area); 
  • //對應的xml,#{0}表明接收的是dao層中的第一個參數,#{1}表明dao層中第二參數,更多參數一致日後加便可。
 <select id="selectUser"resultMap="BaseResultMap"> select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1} </select> 
  • 第2種: 使用 @param 註解: 
  •     import org.apache.ibatis.annotations.param; 
public interface usermapper { user selectuser(@param(「username」) string username, @param(「hashedpassword」) string hashedpassword); }
而後,就能夠在xml像下面這樣使用(推薦封裝爲一個map,做爲單個參數傳遞給mapper):
<select id=」selectuser」 resulttype=」user」> select id, username, hashedpassword from some_table where username = #{username} and hashedpassword = #{hashedpassword} </select>

十、Mybatis動態sql是作什麼的?都有哪些動態sql?能簡述一下動態sql的執行原理不?

Mybatis動態sql可讓咱們在Xml映射文件內,以標籤的形式編寫動態sql,完成邏輯判斷和動態拼接sql的功能。
Mybatis提供了9種動態sql標籤:trim|where|set|foreach|if|choose|when|otherwise|bind。
其執行原理爲,使用OGNL從sql參數對象中計算表達式的值,根據表達式的值動態拼接sql,以此來完成動態sql的功能。

十一、Mybatis的Xml映射文件中,不一樣的Xml映射文件,id是否能夠重複?

不一樣的Xml映射文件,若是配置了namespace,那麼id能夠重複;若是沒有配置namespace,那麼id不能重複;畢竟namespace不是必須的,只是最佳實踐而已。

緣由就是namespace+id是做爲Map<String, MappedStatement>的key使用的,若是沒有namespace,就剩下id,那麼,id重複會致使數據互相覆蓋。有了namespace,天然id就能夠重複,namespace不一樣,namespace+id天然也就不一樣。

十二、爲何說Mybatis是半自動ORM映射工具?它與全自動的區別在哪裏?

Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,能夠根據對象關係模型直接獲取,因此它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,須要手動編寫sql來完成,因此,稱之爲半自動ORM映射工具。

1三、 一對1、一對多的關聯查詢 ?

<mapper namespace="com.lcb.mapping.userMapper"> <!--association 一對一關聯查詢 --> <select id="getClass" parameterType="int" resultMap="ClassesResultMap"> select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id} </select> <resultMap type="com.lcb.user.Classes" id="ClassesResultMap"> <!-- 實體類的字段名和數據表的字段名映射 --> <id property="id" column="c_id"/> <result property="name" column="c_name"/> <association property="teacher" javaType="com.lcb.user.Teacher"> <id property="id" column="t_id"/> <result property="name" column="t_name"/> </association> </resultMap> <!--collection 一對多關聯查詢 --> <select id="getClass2" parameterType="int" resultMap="ClassesResultMap2"> select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id} </select> <resultMap type="com.lcb.user.Classes" id="ClassesResultMap2"> <id property="id" column="c_id"/> <result property="name" column="c_name"/> <association property="teacher" javaType="com.lcb.user.Teacher"> <id property="id" column="t_id"/> <result property="name" column="t_name"/> </association> <collection property="student" ofType="com.lcb.user.Student"> <id property="id" column="s_id"/> <result property="name" column="s_name"/> </collection> </resultMap> </mapper> 

 

Xml映射文件中,除了常見的select|insert|updae|delete標籤以外,還有哪些標籤?

答:還有不少其餘的標籤,<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上動態sql的9個標籤,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>爲sql片斷標籤,經過<include>標籤引入sql片斷,<selectKey>爲不支持自增的主鍵生成策略標籤。

 

 

什麼是Mybatis?

MyBatis是一個支持普通SQL查詢存儲過程高級映射的優秀持久層框架。MyBatis消除了幾乎全部的JDBC代碼和參數的手工設置以及對結果集的檢索封裝。MyBatis可使用簡單的XML或註解用於配置和原始映射,將接口和Java的POJO(Plain Old Java Objects,普通的Java對象)映射成數據庫中的記錄。

mybatis是一個優秀的基於java的持久層框架,它內部封裝了jdbc,使開發者只須要關注sql語句自己,而不須要花費精力去處理加載驅動、建立鏈接、建立statement等繁雜的過程。

mybatis經過xml或註解的方式將要執行的各類statement配置起來,並經過java對象和statement中sql的動態參數進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射爲java對象並返回。

什麼是hibernate?

hibernate是數據訪問層的框架,對jdbc進行了封裝,使用hibernate能夠直接訪問對象,hibernate自動將此訪問轉換爲sql執行,從而達到間接訪問數據庫的目的,簡化了數據訪問層的代碼開發。

hibernate和mybatis對比:

共性:採用ORM思想解決了實體和數據庫映射的問題,對jdbc進行了封裝,屏蔽了jdbc api底層訪問細節,使咱們不用與jdbc api打交道,就能夠完成對數據庫的持久化操做。

Hibernate是全自動化ORM的映射工具(對象關係映射(英語:(Object Relational Mapping,簡稱ORM )

 

1.Hibernate是全自動,而MyBatis是半自動 

Hibernate徹底能夠經過對象關係模型實現對數據庫的操做,擁有完整的JavaBean對象與數據庫的映射結構來自動生成SQL語句。而MyBatis僅有基本的字段映射,對象數據以及對象實際關係仍然須要經過定製SQL語句來實現和管理。

2.Hibernate數據庫移植性遠大於MyBatis。

Hibernate經過它強大的映射結構和hql語言,大大下降了對象與數據庫(OracleMySQL等)的耦合性,而MyBatis因爲須要手寫sql,所以與數據庫的耦合性直接取決於程序員寫SQL的方法,若是SQL不具通用性而用了不少某數據庫特性的sql語句的話,移植性也會隨之下降不少,成本很高。

3.Hibernate擁有完整的日誌系統,MyBatis則欠缺一些。

Hibernate日誌系統很是健全,涉及普遍,包括:SQL記錄、關係異常、優化警告、緩存提示、髒數據警告等;而MyBatis則除了基本記錄功能外,功能薄弱不少。

4.MyBatis相比Hibernate須要關心不少細節

Hibernate配置要比MyBatis複雜的多,學習成本也比MyBatis高。但也正由於MyBatis使用簡單,才致使它要比Hibernate關心不少技術細節。MyBatis因爲不用考慮不少細節,開發模式上與傳統jdbc區別很小,所以很容易上手並開發項目,但忽略細節會致使項目前期bug較多,於是開發出相對穩定的軟件很慢,而開發出軟件卻很快。Hibernate則正好與之相反。可是若是使用Hibernate很熟練的話,實際上開發效率絲絕不差於甚至超越MyBatis。

5.SQL直接優化上,MyBatis要比Hibernate方便不少

因爲MyBatis的sql都是寫在xml裏,所以優化sql比Hibernate方便不少。而Hibernate的sql不少都是自動生成的,沒法直接維護sql;雖有hql,但功能仍是不及sql強大,見到報表等變態需求時,hql也歇菜,也就是說hql是有侷限的;hibernate雖然也支持原生sql,但開發模式上卻與orm不一樣,須要轉換思惟,所以使用上不是很是方便。總之寫sql的靈活度上Hibernate不及MyBatis。

安全性,Hibernate是預編譯的,MyBatis可能存在SQL注入問題,另外使用Hibernate對數據庫類型進行切換時,成本明顯低於MyBatis




 

第一方面:開發速度的對比

就開發速度而言,Hibernate的真正掌握要比Mybatis來得難些。Mybatis框架相對簡單很容易上手,但也相對簡陋些。我的以爲要用好Mybatis仍是首先要先理解好Hibernate。

比起二者的開發速度,不只僅要考慮到二者的特性及性能,更要根據項目需求去考慮究竟哪個更適合項目開發,好比:一個項目中用到的複雜查詢基本沒有,就是簡單的增刪改查,這樣選擇hibernate效率就很快了,由於基本的sql語句已經被封裝好了,根本不須要你去寫sql語句,這就節省了大量的時間,可是對於一個大型項目,複雜語句較多,這樣再去選擇hibernate就不是一個太好的選擇,選擇mybatis就會加快許多,並且語句的管理也比較方便。

第二方面:開發工做量的對比

Hibernate和MyBatis都有相應的代碼生成工具。能夠生成簡單基本的DAO層方法。針對高級查詢,Mybatis須要手動編寫SQL語句,以及ResultMap。而Hibernate有良好的映射機制,開發者無需關心SQL的生成與結果映射,能夠更專一於業務流程。

第三方面:sql優化方面

Hibernate的查詢會將表中的全部字段查詢出來,這一點會有性能消耗。Hibernate也能夠本身寫SQL來指定須要查詢的字段,但這樣就破壞了Hibernate開發的簡潔性。而Mybatis的SQL是手動編寫的,因此能夠按需求指定查詢的字段。

Hibernate HQL語句的調優須要將SQL打印出來,而Hibernate的SQL被不少人嫌棄由於太醜了。MyBatis的SQL是本身手動寫的因此調整方便。但Hibernate具備本身的日誌統計。Mybatis自己不帶日誌統計,使用Log4j進行日誌記錄。

第四方面:對象管理的對比

Hibernate 是完整的對象/關係映射解決方案,它提供了對象狀態管理(state management)的功能,使開發者再也不須要理會底層數據庫系統的細節。也就是說,相對於常見的 JDBC/SQL 持久層方案中須要管理 SQL 語句,Hibernate採用了更天然的面向對象的視角來持久化 Java 應用中的數據。

換句話說,使用 Hibernate 的開發者應該老是關注對象的狀態(state),沒必要考慮 SQL 語句的執行。這部分細節已經由 Hibernate 掌管穩當,只有開發者在進行系統性能調優的時候才須要進行了解。而MyBatis在這一塊沒有文檔說明,用戶須要對對象本身進行詳細的管理。
第五方面:緩存機制

Hibernate緩存

Hibernate一級緩存是Session緩存,利用好一級緩存就須要對Session的生命週期進行管理好。建議在一個Action操做中使用一個Session。一級緩存須要對Session進行嚴格管理。

Hibernate二級緩存是SessionFactory級的緩存。 SessionFactory的緩存分爲內置緩存和外置緩存。內置緩存中存放的是SessionFactory對象的一些集合屬性包含的數據(映射元素據及預約SQL語句等),對於應用程序來講,它是隻讀的。外置緩存中存放的是數據庫數據的副本,其做用和一級緩存相似.二級緩存除了之內存做爲存儲介質外,還能夠選用硬盤等外部存儲設備。二級緩存稱爲進程級緩存或SessionFactory級緩存,它能夠被全部session共享,它的生命週期伴隨着SessionFactory的生命週期存在和消亡。

MyBatis緩存

MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。MyBatis 3 中的緩存實現的不少改進都已經實現了,使得它更增強大並且易於配置。

默認狀況下是沒有開啓緩存的,除了局部的 session 緩存,能夠加強變現並且處理循環 依賴也是必須的。要開啓二級緩存,你須要在你的 SQL 映射文件中添加一行:  <cache/>

字面上看就是這樣。這個簡單語句的效果以下:

  1. 映射語句文件中的全部 select 語句將會被緩存。
  2. 映射語句文件中的全部 insert,update 和 delete 語句會刷新緩存。
  3. 緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
  4. 根據時間表(好比 no Flush Interval,沒有刷新間隔), 緩存不會以任什麼時候間順序 來刷新。
  5. 緩存會存儲列表集合或對象(不管查詢方法返回什麼)的 1024 個引用。
  6. 緩存會被視爲是 read/write(可讀/可寫)的緩存,意味着對象檢索不是共享的,而 且能夠安全地被調用者修改,而不干擾其餘調用者或線程所作的潛在修改。

全部的這些屬性均可以經過緩存元素的屬性來修改。

好比: <cache  eviction=」FIFO」  flushInterval=」60000″  size=」512″  readOnly=」true」/>

這個更高級的配置建立了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,並且返回的對象被認爲是隻讀的,所以在不一樣線程中的調用者之間修改它們會 致使衝突。可用的收回策略有, 默認的是 LRU:

  1. LRU – 最近最少使用的:移除最長時間不被使用的對象。
  2. FIFO – 先進先出:按對象進入緩存的順序來移除它們。
  3. SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
  4. WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。

flushInterval(刷新間隔)能夠被設置爲任意的正整數,並且它們表明一個合理的毫秒 形式的時間段。默認狀況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。

size(引用數目)能夠被設置爲任意正整數,要記住你緩存的對象數目和你運行環境的 可用內存資源數目。默認值是1024。

readOnly(只讀)屬性能夠被設置爲 true 或 false。只讀的緩存會給全部調用者返回緩 存對象的相同實例。所以這些對象不能被修改。這提供了很重要的性能優點。可讀寫的緩存 會返回緩存對象的拷貝(經過序列化) 。這會慢一些,可是安全,所以默認是 false。

相同點:Hibernate和Mybatis的二級緩存除了採用系統默認的緩存機制外,均可以經過實現你本身的緩存或爲其餘第三方緩存方案,建立適配器來徹底覆蓋緩存行爲。

不一樣點:Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細配置,而後再在具體的表-對象映射中配置是那種緩存。

MyBatis的二級緩存配置都是在每一個具體的表-對象映射中進行詳細配置,這樣針對不一樣的表能夠自定義不一樣的緩存機制。而且Mybatis能夠在命名空間中共享相同的緩存配置和實例,經過Cache-ref來實現。

二者比較:由於Hibernate對查詢對象有着良好的管理機制,用戶無需關心SQL。因此在使用二級緩存時若是出現髒數據,系統會報出錯誤並提示。

而MyBatis在這一方面,使用二級緩存時須要特別當心。若是不能徹底肯定數據更新操做的波及範圍,避免Cache的盲目使用。不然,髒數據的出現會給系統的正常運行帶來很大的隱患。

第六方面:總結

對於總結,你們能夠到各大java論壇去看一看

相同點:Hibernate與MyBatis均可以是經過SessionFactoryBuider由XML配置文件生成SessionFactory,而後由SessionFactory 生成Session,最後由Session來開啓執行事務和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命週期都是差很少的。

  • Hibernate和MyBatis都支持JDBC和JTA事務處理。

Mybatis優點

  • MyBatis能夠進行更爲細緻的SQL優化,能夠減小查詢字段。
  • MyBatis容易掌握,而Hibernate門檻較高。

Hibernate優點

  • Hibernate的DAO層開發比MyBatis簡單,Mybatis須要維護SQL和結果映射。
  • Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。
  • Hibernate數據庫移植性很好,MyBatis的數據庫移植性很差,不一樣的數據庫須要寫不一樣SQL。
  • Hibernate有更好的二級緩存機制,可使用第三方緩存。MyBatis自己提供的緩存機制不佳。

他人總結

  • Hibernate功能強大,數據庫無關性好,O/R映射能力強,若是你對Hibernate至關精通,並且對Hibernate進行了適當的封裝,那麼你的項目整個持久層代碼會至關簡單,須要寫的代碼不多,開發速度很快,很是爽。
  • Hibernate的缺點就是學習門檻不低,要精通門檻更高,並且怎麼設計O/R映射,在性能和對象模型之間如何權衡取得平衡,以及怎樣用好Hibernate方面須要你的經驗和能力都很強才行。
  • iBATIS入門簡單,即學即用,提供了數據庫查詢的自動對象綁定功能,並且延續了很好的SQL使用經驗,對於沒有那麼高的對象模型要求的項目來講,至關完美。

 

  • iBATIS的缺點就是框架仍是比較簡陋,功能尚有缺失,雖然簡化了數據綁定代碼,可是整個底層數據庫查詢實際仍是要本身寫的,工做量也比較大,並且不太容易適應快速數據庫修改。
***此文爲轉載,以作學習***
相關文章
相關標籤/搜索