1.resultType 和 和 p resultMap 的区别?
resultType 是<select>标签的一个属性,适合简单对象(POJO、JDK 自带类型:Integer、String、Map 等),只能自动映射,适合单表简单查询。
eg:
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
resultMap 是一个可以被引用的标签,适合复杂对象,可指定映射关系,适合关联复合查询。
eg:
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
2.collection 和 和 association 的区别?
association:一对一
eg:
<resultMap id="blogResult" type="Blog">
<id property="id" column="id" />
<result property="title" column="title"/>
<association property="author" javaType="Author" resultSet="authors" column="author_id" foreignColumn="id">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="bio" column="bio"/>
</association>
</resultMap>
collection:一对多、多对多
<!-- 查询文章带评论的结果(一对多) -->
<resultMap id ="BlogWithCommentMap" type ="com.vincent.domain.associate.BlogAndComment" extends ="BaseResultMap" >
<collection property ="comment" ofType ="com.vincent.domain.Comment">
<id column ="comment_id" property ="commentId" />
<result column ="content" property ="content" />
</collection>
</resultMap>
<!-- 按作者查询文章评论的结果(多对多) -->
<resultMap id ="AuthorWithBlogMap" type ="com.vincent.domain.associate.AuthorAndBlog" >
<id column ="author_id" property ="authorId" jdbcType ="INTEGER"/>
<result column ="author_name" property ="authorName" jdbcType ="VARCHAR"/>
<collection property ="blog" ofType ="com.vincent.domain.associate.BlogAndComment">
<id column ="bid" property ="bid" />
<result column ="name" property ="name" />
<result column ="author_id" property ="authorId" />
<collection property ="comment" ofType ="com.vincent.domain.Comment">
<id column ="comment_id" property ="commentId" />
<result column ="content" property ="content" />
</collection>
</collection>
</resultMap>
3.PrepareStatement 和 和 Statement 的区别?
1).两个都是接口,PrepareStatement 是继承自 Statement 的;
2).Statement 处理静态 SQL,PreparedStatement 主要用于执行带参数的语句;
3).PreparedStatement 的 addBatch()方法一次性发送多个查询给数据库;
4).PreparedStatement 相似 SQL 只编译一次(对语句进行了缓存,相当于一个函数),减少编译次数;
5).PrepareStatement 可以防止 SQL 注入;
6).MyBatis 默认值:PREPARED;
4.MyBatis 解决了什么问题?
1)资源管理(底层对象封装和支持数据源)
2)结果集自动映射
3)SQL 与代码分离,集中管理
4)参数映射和动态 SQL
5)其他:缓存、插件等
5.MyBatis编程式开发中的核心对象及其作用?
SqlSessionFactoryBuilder 创建工厂类
SqlSessionFactory 创建会话
SqlSession 提供操作接口
MapperProxy 代理 Mapper 接口后,用于找到 SQL 执行
6.java类型和数据库类型怎么实现互相映射?
通过TypeHandler,例如 Java 类型中的 String 要保存成 varchar,就会自动调用相应的 Handler。如果没有系统自带的 TypeHandler,也可以自定义。
TypeHandler.png
7.SIMPLE / REUSE / BATCH 三种执行器的区别?
SimpleExecutor 使用后直接关闭 Statement:closeStatement(stmt);
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
ReuseExecutor 放在缓存中,可复用:PrepareStatement#getStatement()
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//调用prepareStatement
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
//调用getStatement,从缓存中获取
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
BatchExecutor 支持复用且可以批量执行 update(),通过ps.addBatch()实现handler.batch(stmt);
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
//复用
statementList.add(stmt);
//批量提交
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
8.MyBatis一级缓存与二级缓存的区别?
一级缓存:在同一个会话(SqlSession)中共享,默认开启,维护在 BaseExecutor中。
二级缓存:在同一个 namespace 共享,需要在 Mapper.xml 中开启,维护在CachingExecutor 中。
9.MyBaits 支持哪些数据源类型?
UNPOOLED:不带连接池的数据源。
POOLED : 带 连 接 池 的 数 据 源 , 在 PooledDataSource 中 维 护PooledConnection。
JNDI:使用容器的数据源,比如 Tomcat 配置了 C3P0。
自定义数据源:实现 DataSourceFactory 接口,返回一个DataSource。当 MyBatis 集成到 Spring 中的时候,使用 Spring 的数据源。
10.关联查询的延迟加载是怎么实现的?
动态代理(JAVASSIST、CGLIB),在创建实体类对象时进行代理,在调用代理对象的相关方法时触发二次查询。
11.Mybatis翻页的几种方法和区别?
逻辑翻页:通过 RowBounds 对象。
物理翻页:通过改写 SQL 语句,可用插件拦截 Executor 实现。
12.怎么解决表字段变化引起的MBG(Mybatis Generator) 文件变化的问题?
Mapper 继 承:自 动 生 成 的 部 分 不 变 , 创 建 接 口 继 承 原 接 口,创 建MapperExt.xml。在继承接口和 MapperExt.xml 中修改。
通用 Mapper:提供支持泛型的通用 Mapper 接口,传入对象类型。
13.解析全局配置文件的时候,做了什么?
创建 Configuration,设置 Configuration
解析 Mapper.xml,设置 MappedStatement
14.接口没有实现类,Mybatis的方法是怎么执行的?
通过MapperProxy代理,代理类的 invoke()方法中调用了 SqlSession.selectOne(),是通过接口名字来找到要执行的statementID的
15.接口方法和映射器的 statement id 是怎么绑定起来的?
MappedStatement 对象中存储了 statement 和 SQL 的映射关系
16.Executor、StatementHandler、ResultsetHandler、ParameterHandler是什么时候创建的?
Executor:在openSession()的时候创建
StatementHandler、ResultsetHandler、ParameterHandler:
执行 SQL 时,在 SimpleExecutor 的 doQuery()中创建
17.ObjectFactory的create() 方法什么时候被调用?
第一次被调用,创建 DefaultResultHandler 的时候:
DefaultResultSetHandler 类中:
handleResultSet new DefaultResultHandler()
第二次被调用,处理结果集的时候:
DefaultResultSetHandler--->handleResultSets--->handleResultSet->
handleRowValues-->handleRowValuesForNestedResultMap-->getRowValue--->createResultObject
18.MyBatis 哪些地方用到了代理模式?
接口查找 SQL:MapperProxy
日志输出:ConnectionLogger、StatementLogger
连接池:PooledDataSource 管理的 PooledConnection
延迟加载:ProxyFactory(JAVASSIST、CGLIB)
插件:Plugin
Spring 集成:SqlSessionTemplate 的内部类 SqlSessionInterceptor
19.MyBatis主要的执行流程?涉及到哪些对象?
Mybatis的执行流程.png
20.MyBatis 插件怎么编写和使用?原理是什么?
使用:继承 Interceptor 接口,加上注解,在 mybatis-config.xml 中配置
原理:动态代理,责任链模式,使用 Plugin 创建代理对象在被拦截对象的方法调用的时候,先执行到 Plugin 的 invoke()方法,再执行到Interceptor 实现类的 intercept()方法,最后通过 Invocation.proceed()方法调用被拦截对象的原方法
21.JDK 动态代理,代理能不能被代理?
能
22.Mybatis 集成到Spring的原理?
SqlSessionTemplate中有内部类SqlSessionInterceptor对DefaultSqlSession进行代理;
MapperFactoryBean 继 承 了 SqlSessionDaoSupport 获 取
SqlSessionTemplate;
接口注册到 IOC 容器中的 beanClass 是 MapperFactoryBean。
23.DefaulSqlSession 和 和 SqlSessionTemplate 的区别是什么?
1)为什么 SqlSessionTemplate 是线程安全的?
因为其内部类 SqlSessionInterceptor 的 invoke()方法中的 getSqlSession()方法:
如果当前线程已经有存在的 SqlSession 对象,会在 ThreadLocal 的容器中拿到SqlSessionHolder,获取 DefaultSqlSession。
如果没有,则会 new 一个 SqlSession,并且绑定到SqlSessionHolder,放到ThreadLocal 中。
SqlSessionTemplate 中在同一个事务中使用同一个 SqlSession。
调用 closeSqlSession()关闭会话时,如果存在事务,减少 holder 的引用计数。否则直接关闭 SqlSession。
2)在编程式的开发中,有什么方法保证 SqlSession 的线程安全?
SqlSessionManager 同时实现了 SqlSessionFactory、SqlSession 接口,通过ThreadLocal 容器维护 SqlSession。
24.常用的注解有哪些?什么时候用注解?什么时候用xml配置?
常用注解:@Insert、@Select、@Update、@Delete、@Param、@Results、@Result
在 MyBatis 的工程中,我们有两种配置 SQL 的方式。一种是在 Mapper.xml 中集中管理,一种是在 Mapper 接口上,用注解方式配置 SQL。
注解的缺点是 SQL 无法集中管理,复杂的 SQL 很难配置。
在业务复杂的项目中只使用 XML 配置的形式,业务简单的项目中可以使用注解和 XML 混用的形式。
25.Mapper 接口无法注入或 Invalid bound statement (not found)该怎么解决?
1)扫描配置,xml 文件和 Mapper 接口有没有被扫描到
2)namespace 的值是否和接口全类名一致
3)检查对应的 sql 语句 ID 是否存在
26.怎么获取插入的最新自动生成的ID?
常见的做法是执行一次查询,max 或者 order by 倒序获取最大的 ID(存在低效、存在并发问题)。
在 MyBatis 里面还有一种更简单的方式:
<insert id="insert" parameterType ="com.vincent.domain.Blog">
insert into blog (bid, name, author_id)
values (#{bid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{author,jdbcType=CHAR})
</insert>
代码:
blogService.addBlog(blog);
System.out.println(blog.getBid());
27.SQL如何实现模糊查询LIKE?
1)字符串拼接
在 Java 代码中拼接%%(比如 name = "%" + name + " %" ; ),直接 LIKE。因为没有预编译,存在 SQL 注入的风险,不推荐使用。
2)CONCAT(推荐)
<when teste ="empName != null and empName = != '' ">
AND e.emp_name LIKE CONCAT(CONCAT('%', #{emp_name, jdbcType=VARCHAR}),'%')
</when>
3)bind 标签
<select id="getEmpList_bind" resultType="empResultMap" parameterType ="Employee">
<bind name="pattern1" value="'%' + empName + '%'" />
<bind name="pattern2" value="'%' + email + " '%'" />
SELECT * FROM tbl_emp
<where>
<if test ="empId != null">
emp_id = #{empId,jdbcType=INTEGER},
</if>
<if test ="empName != null and empName != ''">
AND emp_name LIKE #{pattern1}
</if>
<if test ="email != null and email != ''">
AND email LIKE #{pattern2}
</if>
</where>
ORDER BY emp_id
</select>
28.什么 时候用 #{} ? 什么时候用 {}。作为 OGNL 表达式,都可以实现参数的替换。
这两个符号的解析方式是不一样的:
会解析为 Prepared Statement 的参数标记符,参数部分用?代替。传入的参数会经过类型检查和安全检查。
$只会做字符串替换
区别:
1、 是否能防止 SQL 注入:方式没有预编译,不会缓存
结论:
1、 能用#的地方都用#
2、 常量的替换,比如排序条件中的字段名称,不用加单引号,可以使用$
29.对象 属性是基本类型int、double ,数据库返回 null 是 报错?
要使用包装类型,如Integer,不要使用基本类型如int
30.XML中怎么使用特殊符号,如 < & 等?
- 转义< < (大于可以直接写)
- 使用<![CDATA[ ]]>——当 XML 遇到这种格式就会把[]里面的内容原样输出,不
进行解析
网友评论