zoukankan      html  css  js  c++  java
  • Mybatis

    1.Mybatis的架构

    1.1 Mybatis的框架分层

    这里写图片描述

    1.2 MyBatis的实现原理

    mybatis底层还是采用原生jdbc来对数据库进行操作的,只是通过 SqlSessionFactory,SqlSession Executor,StatementHandler,ParameterHandler,ResultHandler和TypeHandler等几个处理器封装了这些过程

    
        执行器: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进行映射。如下图: 
    这里写图片描述

    2.Mybatis工作过程

    通过读mybatis的源码进行分析mybatis的执行操作的整个过程,我们通过debug调试就可以知道Mybatis每一步做了什么事,我先把debug每一步结果 截图,然后在分析这个流程。 
    第一步:读取配置文件,形成InputStream

    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的时序图: 
    这里写图片描述

    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里面执行

    //判断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的方法

       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;
            }
      

    1、#{}和${}的区别是什么?

    #{}是预编译处理,${}是字符串替换。
    Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
    Mybatis在处理${}时,就是把${}替换成变量的值。
    使用#{}可以有效的防止SQL注入,提高系统安全性。
    

    2、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

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

    3、 模糊查询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>

    4、通常一个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执行结果返回。
    

    5、Mybatis是如何进行分页的?分页插件的原理是什么?

    Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
    
    分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
    

    6、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

    答:第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
    
    有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
    

    7、如何执行批量插入?

    首先,创建一个简单的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(); 
        }

    8、如何获取自动生成的(主)键值?

    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());

    9、在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>

    10、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的功能。
    

    11、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

    不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。
    
    原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
    

    12、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

    Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
    

    13、 一对一、一对多的关联查询 ?

    <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的缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
    ***此文为转载,以做学习***
  • 相关阅读:
    选择排序——Java实现
    冒泡排序——Python实现
    基数排序——Java实现
    Sqoop迁移Hadoop与RDBMS间的数据
    Hive Join
    Hadoop 完全分布式部署(三节点)
    springboot自定义异常处理
    linux下安装redis
    windows 下tomcat重启脚本
    解决rabbin首次请求慢的问题
  • 原文地址:https://www.cnblogs.com/danica/p/9330492.html
Copyright © 2011-2022 走看看