1、Mybatis是一个半ORM(对象关系映射)持久层框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
持久化:程序的数据瞬时状态(内存中,断电即失)==》持久态(数据库中、文件中)
2、通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
3、优点:
SQL语句编程,相当灵活,sql和代码分离
与Spring很好的集成
提供映射标签,支持对象与数据库的ORM字段关系映射
与各种数据库兼容
简化jdbc代码
4、不足:
依赖数据库,不能随便更换数据库
需要编写sql,有编写sql的能力
5、#{}和${}的区别是什么?
#{} 是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
6、当实体类中的属性名和表中的字段名不一样 ?
第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
第2种: 通过 <resultMap> 来映射字段名和实体类属性名的一一对应的关系
7、Mapper 接口的工作原理
是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。
8、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式
第一种是使用 <resultMap> 标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
9、在mapper中如何传递多个参数
1、第一种:
DAO层的函数
2、第二种: 使用 @param 注解:
然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
3、第三种:多个参数封装成map
10、Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签: trim|where|set|foreach|if|choose|when|otherwise|bind。
11、Mybatis是否支持延迟加载
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false
原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法
12、工作原理:
SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory
SqlSessionFactory 文开启一个SqlSession,实例获得Mapper对象并运行mapper映射的SQl语句。完成数据库的crud操作和事务提交,关闭SqlSession
1)读取 MyBatis 配置文件;mybatis-config.xml
2)加载映射文件:SQL 映射文件
3)构造会话工厂:构建会话工厂 SqlSessionFactory
4)创建会话对象:建 SqlSession 对象,包含执行 SQL 语句的所有方法
5)Executor 执行器:Executor 接口来操作数据库,根据 SqlSession 传递的参数动态地生成要执行的 SQL 语句
6)MappedStatement 对象:存储要映射的 SQL 语句的 id、参数等信息
7)输入参数映射: Map、 List 等集合类型,也可以基本数据类型和 POJO 类型
8)输出结果映射: Map、 List 等集合类型,也可以基本数据类型和 POJO 类型
13、Mybatis分页原理:
通过我们设置的RowBounds来返回分页结果的
原理:
将所有结果查询出来,然后通过计算offset和limit,只返回部分结果,操作在内存中进行,所以也叫内存分页
缺点:数据量大,不行
物理分页:
直接在SQL加limit
14、MyBatis编程步骤是什么样的?
创建SqlSessionFactory——>创建SqlSession ——>执行数据库操作——> session.commit()提交事务——> session.close()关闭会话
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例
通过sqlswssionfactory获得sqlsession,sqlsession提供了在数据库执行sql命令所有方法,通过sqlsession直接执行已经映射的SQL语句
mybatis-config.xml
environment 元素体中包含了事务管理和连接池的配置
命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了接口绑定。
XML方式
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select></mapper>
注解方式:适用于简单的SQL
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);}
SqlSessionFactoryBuilder类
可以实例化、使用和丢弃,创建了SqlSessionFactory之后,不再需要,作用域:方法作用域,局部方法变量,利用它创建多个SqlSessionFactory,然后释放。
SqlSessionFactoryBuilder 有五个 build() 方法,每一种都允许你从不同的资源中创建一个 SqlSessionFactory 实例
SqlSessionFactory类
一旦创建,运行期间,一直存在,不要多次重建,作用域:应用作用域,多种实现:
最简单的就是使用单例模式或者静态单例模式。
String resource = "org/mybatis/example/mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactory 有六个方法创建 SqlSession 实例。
SqlSession
每个线程都有自己的SqlSession实例,线程不安全,不能共享,作用域:请求或方法作用域,不能放在类的静态域或者类的实例变量,每次收到HTTP请求 ,就打开一个SqlSession,返回响应,就关闭,放到finally中。
SqlSession提供select/insert/update/delete方法,在旧版本中使用使用SqlSession接口的这些方法,但是新版的Mybatis中建议使用Mapper接口的方法,底层还是采用SqlSession接口方法实现的
执行包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。
通过这个接口来执行命令,获取映射器示例和管理事务
try (SqlSession session = sqlSessionFactory.openSession()) {
// 应用逻辑代码}
映射器其实就是一个动态代理对象
映射器接口的实例是从 SqlSession 中获得的
configuration(配置)
加载顺序:
如果一个属性在不只一个地方进行了配置,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。
<property name="username" value="${username:ut_user}"/>
这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。
<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 --></properties>
<settings>
<setting name="cacheEnabled" value="true"/> //开启缓存
<setting name="lazyLoadingEnabled" value="true"/> //开启延迟加载
<setting name="multipleResultSetsEnabled" value="true"/>//允许单个语句返回多结果集
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>//允许 JDBC 支持自动生成主键
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>//设置超时时间
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>//是否开启驼峰命名自动映射
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/></settings>
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/></typeAliases>
别名包 :默认是bean的小写,大写也可以
<typeAliases>
<package name=”com.wang.entity”></typeAliases>
注解的别名优先级更高
@Alias(“hello”) 实体类上
得知该类型处理器处理的 Java 类型
通过两种方式来指定关联的 JDBC 类型
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。
可以配置多个环境,每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果想连接两个数据库,就需要创建两个 SqlSessionFactory 实例。
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/></transactionManager>
使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
数据源(dataSource)
三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用
可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源实现
直接告诉 MyBatis 到哪里去找映射文件。 可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///形式的 URL),或类名和包名等。
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/></mappers>
<!-- 使用完全限定资源定位符(URL) --><mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/></mappers>
<!-- 使用映射器接口实现类的完全限定类名 --><mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/></mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 --><mappers>
<package name="org.mybatis.builder"/></mappers>
<select
id="selectPerson" //唯一标识
parameterType="int" //传入语句的参数的类的全限定名或别名 ,可选通过类型处理器推断。
parameterMap="deprecated"//废弃,使用行内参数映射和parameter Type代替
resultType="hashmap" //希望返回的类的全限定名或别名,返回集合,则返回集合包含的类型,
不是结合本身,和resultMap只能有一个
resultMap="personResultMap"//外部resultmap的引用,最强大的特性
flushCache="false"// 设置为true ,语句被调用,会导致本地缓存和二级缓存被清空
useCache="true"//语句结果会被二级缓存缓存起来
timeout="10"//超时时间,抛异常之前,程序等待数据库相应的时间,默认:unset未设置
fetchSize="256"//建议值,让驱动程序每次返回结果的行数等于这个建议值
statementType="PREPARED"//STATEMENT,PREPARED 或 CALLABLE,默认值PREPARED,
resultSetType="FORWARD_ONLY">
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法 来取出由数据库内部生成的主键 (比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段) ,默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值, 默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
如果数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就可以。例如,
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})</insert>
数据库还支持多行插入, 你也可以传入一个 Author 数组或集合,并返回自动生成的主键。
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach></insert>
默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候可以
ORDER BY ${columnName}
如果想 select 一个表任意一列的数据时
@Select("select * from user where ${column} = #{value}")User findByColumn(@Param("column") String column, @Param("value") String value);
其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理
连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系。
一个javabean 三个属性
id,username 和 hashedPassword。映射resultset
<!-- mybatis-config.xml 中 --><typeAlias type="com.someapp.model.User" alias="User"/>
<!-- SQL 映射 XML 中 --><select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}</select>
MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}</select>
解决列名不匹配:上面的隐式配置,在sql语句给列名起一个别名
解决列名不匹配的另外一种方式:需要显式的配置resultmap
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/></resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}</select
数据库不可能永远是你所想或所需的那个样子,并不都是每个数据库都具备良好的第三范式或 BCNF 范式,一种数据库映射模式,完美适配所有的应用程序,ResultMap 就是 MyBatis 对这个问题的答案。
结果映射(resultMap)
constructor - 用于在实例化类时,注入结果到构造方法中
idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
arg - 将被注入到构造方法的一个普通结果
id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result – 注入到字段或 JavaBean 属性的普通结果
association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection – 一个复杂类型的集合
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
discriminator – 使用结果值来决定使用哪个 resultMap
case – 基于某些值的结果映射
嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
Id 和 Result 的属性 |
|
属性 |
描述 |
property |
映射到列结果的字段或属性。 JavaBean 有这个名字的属性(property), 先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 可以这样映射简单的东西:“username”,或者映射到复杂的东西:“address.street.number”。 |
column |
数据库中的列名,或别名。和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType |
一个 Java 类的全限定名,或一个类型别名(如果你映射到一个 JavaBean MyBatis 通常可以推断类型。 如果你映射到的是 HashMap,你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。 这是 JDBC 的要求而非 MyBatis 的要求。 直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
typeHandler |
默认的类型处理器。使用这个属性,可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名 |
构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。
<association property="author" column="blog_author_id" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/></association>
MyBatis 有两种不同的方式加载关联:
javaType |
一个 Java 类的完全限定名,或一个类型别名,如果你映射到一个 JavaBean, MyBatis 通常可以推断类型。如果映射到的是 HashMap, 应该明确地指定 javaType 来保证行为与期望的相一致。 |
示例:
我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性,其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/></resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}</select>
会导致N+1问题,但是mybatis提供了延时加载,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,会触发所有的延迟加载查询,性能可能会变得很糟糕。
另一种解决方式:关联的嵌套结果映射
将博客表和作者表连接在一起,而不是执行一个独立的查询语句
<select id="selectBlog" resultMap="blogResult">
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
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<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"/>
</association></resultMap>
从版本 3.2.3 开始,MyBatis 提供了另一种解决 N+1 查询问题的方法。
存储过程执行下面的查询并返回两个结果集。第一个结果集会返回博客(Blog)的结果,第二个则返回作者(Author)的结果。
在映射语句中,必须通过 resultSets 属性为每个结果集指定一个名字,多个名字使用逗号隔开。
<select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
{call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}</select>
<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>
嵌套 Select 查询来为博客加载文章。
<resultMap id="blogResult" type="Blog">
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/></resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}</select>
<select id="selectPostsForBlog" resultType="Post">
SELECT * FROM POST WHERE BLOG_ID = #{id}</select>
“ofType” 属性。这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
P.id as post_id,
P.subject as post_subject,
P.body as post_body,
from Blog B
left outer join Post P on B.id = P.blog_id
where B.id = #{id}</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection></resultMap>
单的场景,自动映射查询结果。复杂的场景,你需要构建一个结果映射。 可以混合使用这两种策略。
有三种自动映射等级:
默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。
通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这个简单语句的效果如下:
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if></select>
不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose></select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where></select>
可以通过自定义 trim 元素来定制 where 元素的功能
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。会删掉额外的逗号
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}</update>
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach></select>
将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值
配置了 databaseIdProvider
<insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})</insert>
org.apache.ibatis.binding.BindingException: Type interface com.wang.mapper.UserMapper is not known to the MapperRegistry.
没有注册mapper
解决:
<mappers>
<mapper resource="com/wang/mapper/UserMapper.xml"/>
<mapper class="com.wang.mapper.UserMapper"/>
</mappers>
使用resource最好,写在resource或者和mapper在同一个包下,都可以
使用class,必须mapper和xml同名,且必须在同一个包。
使用package name=”com.wang.mapper”
报错:java.lang.ExceptionInInitializerError
The error may exist in com/wang/mapper/UserMapper.xml
Maven约定大于配置,写的配置文件,可能无法导出或生效
在pom中加上配置
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
增删改操作,需要提交事务 commit
如果字段过多,可以使用Map
Map传递参数,直接在sql中取出key
parameterType="map"
对象传递参数,要在sql中取对象的属性
parameterType="Object"
只有一个基本参数类型参数,直接在sql取出
<select id="selectById" resultType="com.wang.entity.User" parameterType="int">
select * from mybatis.user where id=#{id}
</select>
可以不写paramterType=”int”
多个参数使用Map或者注解
对象:使用
第一种:子查询
第二种:按照结果嵌套处理
<select id="getstudent2" resultMap="studentMap2">
select s.id sid,s.name sname,t.name tname
from student s,teacher t where s.tid=t.id;
</select>
<resultMap id="studentMap2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from mybatis.teacher
</select>
<!--结果嵌套查询-->
<select id="getTeacherAndStu" resultMap="teacherMap">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="teacherMap" type="Teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
<!--javaType:指定属性的类型
集合中的泛型信息,使用ofType属性
-->
<collection property="students" ofType="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!--子查询-->
<select id="getTeacherAndStu1" resultMap="teacherMap2">
select * from mybatis.teacher where id=#{tid}
</select>
<resultMap id="teacherMap2" type="Teacher">
<id property="id" column="id"/>
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentById"/>
</resultMap>
<select id="getStudentById" resultType="Student">
select * from mybatis.student where tid=#{tid}
</select>
控制台打印出sql
日志工厂:默认slf4j|log4j
标准日志工厂实现:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j实现:客人已通过配置文件灵活配置
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
读写分离
主从复制
默认开启一级缓存 基于sqlsession
二级缓存需要手动开启 基于namespace 实现Cache接口
缓存失效:
1、增删改都会刷新缓存
2、查询不同的数据
3、查询不同的mapper.xml
4、清理缓存 sqlsession.clearCache();
开启二级缓存(全局缓存)
<setting name=”cacheEnabled” value=”true”></setting>
Mapper.xml 中<cache/>
顺序:用户进来==》二级缓存==》一级缓存==》数据库
LRU:最近最少使用原则
FIFO:先进先出原则
不同的mapper查询的数据会放在自己对应的map中
Mybatis详细执行流程:
Resource===》加载全局配置文件==》实例化sqlsessionbuilder构造器===》build方法 解析配置文件XMLconfiguration对象的parse()方法来具体解析mybatis配置文件,返回一个 --configuration对象作为参数又返回给一个build方法,返回值是一个sqlsessionfactory类型的 DefaultSql'SessionFactory====》sqlsessionfactory是一个接口 实现类DefaultSql'SessionFactory实例化===》opensession方法返回sqlsession(接口),DefaultSql'SessionFactory类final xecutor执行器用SimpleExecutor来执行,实现类、事务工厂等
Sqlswssion接口中由getMapper(类.class)方法,通过反射获得mapper的方法,根据mapperxml映射执行具体增删改查sql语句
jdk动态代理生成mapper接口的代理对象,通过mapperRegistry的对象,
return mapperProxyFactory.newInstance(sqlSession); 执行代理对象 mapperProxy的invoke方法
return new DefaultSqlSession(configuration, executor, autoCommit);
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
注解:多个简单类型参数 使用@Param注解基本类型和string类型加上,引用类型不需要
@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中
@Select(“selext * from user where id=#{uid}”)
List<user> getUser(@Param(“uid”) int id ,@param(“namae”)string name)
@param( "uid ")是数据库中的字段名,id是类中的属性名
使用注解,可以不用paramterType
public List<UserExtension> selectUser( @Param("user") UserExtension user);
<select id=" selectUser" resultMap="BaseResultMap">
select * from user_user_t where user_name = #{user.userName,jdbcType=VARCHAR} and user_area=#{user.userArea,jdbcType=VARCHAR}
</select>
不使用@Param注解,同样也可以传递多个参数(尤其是多个 javaBean)
1、DAO层的函数方法
Public User selectUser(String name,String area);
对应的Mapper.xml
<select id="selectUser" resultMap="BaseResultMap">
select * from user_user_t where user_name = #{0} and user_area=#{1}
</select>
其中,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数
2、Map传参
Public User selectUser(Map paramMap);
Service层调用
Private User xxxSelectUser(){Map paramMap=new hashMap();paramMap.put(“userName”,”对应具体的参数值”);paramMap.put(“userArea”,”对应具体的参数值”);User user=xxx. selectUser(paramMap);}
<select id=" selectUser" resultMap="BaseResultMap"> select * from user_user_t where user_name = #{userName,jdbcType=VARCHAR} and user_area=#{userArea,jdbcType=VARCHAR} </select>
3、Dao层的函数方法
Public User selectUser(@param(“userName”)String name,@param(“userArea”)String area);
<select id=" selectUser" resultMap="BaseResultMap"> select * from user_user_t where user_name = #{userName,jdbcType=VARCHAR} and user_area=#{userArea,jdbcType=VARCHAR} </select>
@Mapper
public interface UserMapper {
List<User> getAllUsers(@Param("order_by")String order_by);
}
对应的 XML 定义如下:
<select id="getAllUsers" resultType="org.javaboy.helloboot.bean.User">
select * from user
<if test="order_by!=null and order_by!=''">
order by ${order_by} desc
</if>
</select>
在动态 SQL 中使用了参数作为变量,需要 @Param 注解
在动态 SQL 中用到了 参数作为判断条件,要加 @Param 注解的
多对多:用户user==角色role 加中间表user_role
<resultMap id="userMap" type="com.example.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<!--<collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>-->
<collection property="roles" ofType="com.example.domain.Role">
<id property="roleId" column="rid"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="roleDesc" column="ROLE_DESC"/>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT u.*,r.ID as rid,r.ROLE_DESC,r.ROLE_NAME FROM user u
LEFT OUTER JOIN user_role ur on u.id = ur.UID
LEFT OUTER JOIN role r on ur.RID = r.ID
</select>
collection也可以分开写引用结果映射
<resultMap id="userMap" type="com.example.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>
</resultMap>
<resultMap id="roleMap" type="com.example.domain.Role">
<id property="roleId" column="rid"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="roleDesc" column="ROLE_DESC"/>
</resultMap>
<mapper namespace="com.zking.mapper.IPerson">
<!-- resultMap:映射实体类和字段之间的一一对应的关系 -->
<resultMap id="selectPersonList" type="person">
<id column="pid" property="pid"></id>
<result column="pname" property="pname"></result>
<!-- 多对多关联映射:collection -->
<collection property="cityList" ofType="city">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</collection>
</resultMap>
<select id="selectPerson" resultMap="selectPersonList">
SELECT p.*,ci.*,ca.`cno` FROM person p, city ci, card ca
WHERE p.`pid`=ca.`pid` AND ci.`cid`=ca.`cid`
</select>
mybatis中如果返回对象集合的话,会把对象中的所有字段都返回,如果表中字段很多而我只需要部分字段,有几种解决方案
1、重新定义类,里面存放要返回的字段属性
2、将结果定义为List<Map<String, Object>>类型
List<Map<String, Object>> selectPartBook();
<resultMap id="PartBookMap" type="java.util.HashMap">
MyBatis 四大对象(
Executor、拦截内部执行器:
StatementHandler、拦截SQl语法构建
ParameterHandler、拦截参数
ResultSetHandler) 拦截结果集处理
Mybatis分页方式 四种
1、数组分页 全部取出 list截取
2、Sql分页 limit 第几页 记录数
3、拦截器分页 implements Interceptor
4、Rowbounds分页 数据量小使用,大使用拦截器
Rowbounds,并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。
Mybatis逻辑分页核物理分页区别:
物理分页,数据库,效率高,例如mysql的limit 个数据库实现不同 oracle是rownum
逻辑分页:利用游标分页,效率低;
MyBatis使用RowBounds实现的分页是逻辑分页;全查,再offset和limit截断记录返回。
插件分页:Mybatis-PageHelper
Mybatis有三种基本的Executor执行器:
SimpleExecutor、ReuseExecutor、BatchExecutor