1、resultType 和resultMap 的区别?
resultType 是<select>标签的一个属性,适合简单对象(POJO、JDK 自带类型:Integer、String、Map 等),只能自动映射,适合单表简单查询。
<select id="selectAuthor" parameterType="int" resultType="com.demo.domain.Author">
select author_id authorId, author_name authorName
from author where author_id = #{authorId}
</select>
resultMap 是一个可以被引用的标签,适合复杂对象,可指定映射关系,适合关联复合查询。
<resultMap id="BlogWithAuthorResultMap" type="com.demo.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<!-- 联合查询,将author 的属性映射到ResultMap -->
<association property="author" javaType="com.demo.domain.Author">
<id column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
</association>
</resultMap>
2、collection 和association 的区别?
association:一对一
<!-- 另一种联合查询(一对一)的实现,但是这种方式有“N+1”的问题-->
<resultMap id="BlogWithAuthorQueryMap" type="com.demo.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<association property="author" javaType="com.demo.domain.Author" column="author_id" select="selectAuthor"/> <!-- selectAuthor 定义在下面 -->
</resultMap>
collection:一对多、多对多
<!-- 查询文章带评论的结果(一对多) -->
<resultMap id="BlogWithCommentMap" type="com.demo.domain.associate.BlogAndComment" extends="BaseResultMap" >
<collection property="comment" ofType="com.demo.domain.Comment">
<id column="comment_id" property="commentId" />
<result column="content" property="content" />
</collection>
</resultMap>
<!-- 按作者查询文章评论的结果(多对多) -->
<resultMap id="AuthorWithBlogMap" type="com.demo.domain.associate.AuthorAndBlog" >
<id column="author_id" property="authorId" jdbcType="INTEGER"/>
<result column="author_name" property="authorName" jdbcType="VARCHAR"/>
<collection property="blog" ofType="com.demo.domain.associate.BlogAndComment">
<id column="bid" property="bid" />
<result column="name" property="name" />
<result column="author_id" property="authorId" />
<collection property="comment" ofType="com.demo.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、PS 相似SQL 只编译一次(对语句进行了缓存,相当于一个函数),减少编译次数;
5、PS 可以防止SQL 注入;
6、MyBatis 默认值:PREPARED
5、总结:MyBatis 里面用到了哪些设计模式?
设计模式 | 类 |
---|---|
工厂 | SqlSessionFactory、ObjectFactory、MapperProxyFactory |
建造者 | XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuidler |
单例模式 | SqlSessionFactory、Configuration、ErrorContext |
代理模式 | 绑定:MapperProxy 延迟加载:ProxyFactory(CGLIB、JAVASSIT) 插件:Plugin Spring集成 MyBaits:SqlSessionTemplate的内部类SqlSessionInterceptor MyBatis 自带连接池:PooledDataSource管理的 PooledConnection 日志打印:ConnectionLogger、StatementLogger |
适配器模式 | logging模块,对于 Log4j、JDK logging 这些没有直接实现 slf4j 接口的日志组件,需要适配器 |
装饰模板方法模式 | BaseExecutor与子类 SimpleExecutor、BatchExecutor、ReuseExecutor |
装饰器模式 | LoggingCache、LruCache 等对 PerpectualCache的装饰CachingExecutor对其他 Executor的装饰 |
责任链模式 | InterceptorChain |
6、当我们传入RowBounds 做翻页查询的时候,使用limit 物理分页,代替原来的逻辑分页
@Intercepts({@Signature(type = Executor.class,method = “query”,
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println(“将逻辑分页改为物理分页”);
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0]; // MappedStatement
BoundSql boundSql = ms.getBoundSql(args[1]); // Object parameter
RowBounds rb = (RowBounds) args[2]; // RowBounds
// RowBounds为空,无需分页
if (rb == RowBounds.DEFAULT) {
return invocation.proceed();
}
// 将原 RowBounds 参数设为 RowBounds.DEFAULT,关闭 MyBatis 内置的分页机制
//args[2] = RowBounds.DEFAULT;
// 在SQL后加上limit语句
String sql = boundSql.getSql();
String limit = String.format("LIMIT %d,%d", rb.getOffset(), rb.getLimit());
sql = sql + " " + limit;
// 自定义sqlSource
SqlSource sqlSource = new StaticSqlSource(ms.getConfiguration(), sql, boundSql.getParameterMappings());
// 修改原来的sqlSource
Field field = MappedStatement.class.getDeclaredField("sqlSource");
field.setAccessible(true);
field.set(ms, sqlSource);
// 执行被拦截方法
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
7、在未启用日志组件的情况下,输出执行的SQL,并且统计SQL 的执行时间(先实现查询的拦截)
@Intercepts({ @Signature(type = StatementHandler.class, method = "query", args = { Statement.class, ResultHandler.class}) })
public class SQLInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
System.out.println("获取到SQL语句:"+sql);
try {
return invocation.proceed();
}finally {
long endTime = System.currentTimeMillis();
System.out.println("SQL执行耗时:" + (endTime-startTime) +"ms");
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
String name = properties.getProperty("name");
// System.out.println("获取到的参数:"+ name);
}
}
8、用注解还是用xml 配置?
常用注解:@Insert、@Select、@Update、@Delete、@Param、@Results、@Result
在MyBatis 的工程中,我们有两种配置SQL 的方式。一种是在Mapper.xml 中集中管理,一种是在Mapper 接口上,用注解方式配置SQL。很多同学在工作中可能两种方式都用过。那到底什么时候用XML 的方式,什么时候用注解的方式呢?
注解的缺点是SQL 无法集中管理,复杂的SQL 很难配置。所以建议在业务复杂的项目中只使用XML 配置的形式,业务简单的项目中可以使用注解和XML 混用的形式。
9、Mapper 接口无法注入或Invalid bound statement (not found)
我们在使用MyBatis 的时候可能会遇到Mapper 接口无法注入,或者mapper statement id 跟Mapper 接口方法无法绑定的情况。基于绑定的要求或者说规范,我们
可以从这些地方去检查一下:
1、扫描配置,xml 文件和Mapper 接口有没有被扫描到
2、namespace 的值是否和接口全类名一致
3、检查对应的sql 语句ID 是否存在
10、怎么获取插入的最新自动生成的ID
在MySQL 的插入数据使用自增ID 这种场景,有的时候我们需要获得最新的自增ID,比如获取最新的用户ID。常见的做法是执行一次查询,max 或者order by 倒序获取最大的ID(低效、存在并发问题)。在MyBatis 里面还有一种更简单的方式:insert 成功之后,mybatis 会将插入的值自动绑定到插入的对象的Id 属性中,我们用getId 就能取到最新的ID。
<insert id="insert" parameterType="com.demo.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());
11、XML 中怎么使用特殊符号,比如小于&
1、转义< <
(大于可以直接写)
2、使用<![CDATA[ ]]>——当XML 遇到这种格式就会把[]里面的内容原样输出,不进行解析