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
经过读mybatis的源码进行分析mybatis的执行操做的整个过程,咱们经过debug调试就能够知道Mybatis每一步作了什么事,我先把debug每一步结果 截图,而后在分析这个流程。
第一步:读取配置文件,造成InputStreammysql
从debug调试看出 返回的 sqlSessionFactory 是DefaultSesssionFactory类型的,可是configuration此时已经被初始化了。查看源码后画以下建立DefaultSessionFactory的时序图: 程序员
从debug调试 看出SqlSessinoFactory.openSession() 返回的sqlSession是 DefaultSession类型的,此SqlSession里包含一个Configuration的对象,和一个Executor对象。查看源码后画以下建立DefaultSession的时序图:算法
从debug调试能够看出,mapper是一个Mapper代理对象,并且初始化了Configuration对象,Executor的对象。查看源码后画以下建立Mapper的时序图: sql
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);
<select id="selectUser"resultMap="BaseResultMap"> select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1} </select>
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>
答:还有不少其余的标签,<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 )
Hibernate彻底能够经过对象关系模型实现对数据库的操做,拥有完整的JavaBean对象与数据库的映射结构来自动生成SQL语句。而MyBatis仅有基本的字段映射,对象数据以及对象实际关系仍然须要经过定制SQL语句来实现和管理。
Hibernate经过它强大的映射结构和hql语言,大大下降了对象与数据库(Oracle、MySQL等)的耦合性,而MyBatis因为须要手写sql,所以与数据库的耦合性直接取决于程序员写SQL的方法,若是SQL不具通用性而用了不少某数据库特性的sql语句的话,移植性也会随之下降不少,成本很高。
Hibernate日志系统很是健全,涉及普遍,包括:SQL记录、关系异常、优化警告、缓存提示、脏数据警告等;而MyBatis则除了基本记录功能外,功能薄弱不少。
Hibernate配置要比MyBatis复杂的多,学习成本也比MyBatis高。但也正由于MyBatis使用简单,才致使它要比Hibernate关心不少技术细节。MyBatis因为不用考虑不少细节,开发模式上与传统jdbc区别很小,所以很容易上手并开发项目,但忽略细节会致使项目前期bug较多,于是开发出相对稳定的软件很慢,而开发出软件却很快。Hibernate则正好与之相反。可是若是使用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一级缓存是Session缓存,利用好一级缓存就须要对Session的生命周期进行管理好。建议在一个Action操做中使用一个Session。一级缓存须要对Session进行严格管理。
Hibernate二级缓存是SessionFactory级的缓存。 SessionFactory的缓存分为内置缓存和外置缓存。内置缓存中存放的是SessionFactory对象的一些集合属性包含的数据(映射元素据及预约SQL语句等),对于应用程序来讲,它是只读的。外置缓存中存放的是数据库数据的副本,其做用和一级缓存相似.二级缓存除了之内存做为存储介质外,还能够选用硬盘等外部存储设备。二级缓存称为进程级缓存或SessionFactory级缓存,它能够被全部session共享,它的生命周期伴随着SessionFactory的生命周期存在和消亡。
MyBatis 包含一个很是强大的查询缓存特性,它能够很是方便地配置和定制。MyBatis 3 中的缓存实现的不少改进都已经实现了,使得它更增强大并且易于配置。
默认状况下是没有开启缓存的,除了局部的 session 缓存,能够加强变现并且处理循环 依赖也是必须的。要开启二级缓存,你须要在你的 SQL 映射文件中添加一行: <cache/>
字面上看就是这样。这个简单语句的效果以下:
全部的这些属性均可以经过缓存元素的属性来修改。
好比: <cache eviction=”FIFO” flushInterval=”60000″ size=”512″ readOnly=”true”/>
这个更高级的配置建立了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以在不一样线程中的调用者之间修改它们会 致使冲突。可用的收回策略有, 默认的是 LRU:
flushInterval(刷新间隔)能够被设置为任意的正整数,并且它们表明一个合理的毫秒 形式的时间段。默认状况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)能够被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是1024。
readOnly(只读)属性能够被设置为 true 或 false。只读的缓存会给全部调用者返回缓 存对象的相同实例。所以这些对象不能被修改。这提供了很重要的性能优点。可读写的缓存 会返回缓存对象的拷贝(经过序列化) 。这会慢一些,可是安全,所以默认是 false。
相同点:Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,均可以经过实现你本身的缓存或为其余第三方缓存方案,建立适配器来彻底覆盖缓存行为。
不一样点:Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,而后再在具体的表-对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每一个具体的表-对象映射中进行详细配置,这样针对不一样的表能够自定义不一样的缓存机制。而且Mybatis能够在命名空间中共享相同的缓存配置和实例,经过Cache-ref来实现。
二者比较:由于Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。因此在使用二级缓存时若是出现脏数据,系统会报出错误并提示。
而MyBatis在这一方面,使用二级缓存时须要特别当心。若是不能彻底肯定数据更新操做的波及范围,避免Cache的盲目使用。不然,脏数据的出现会给系统的正常运行带来很大的隐患。
第六方面:总结
对于总结,你们能够到各大java论坛去看一看