:src/main/java和src/main/resources是同一个目录
注意:如果是取简单数量类型的参数,括号中的参数名称必须为value
例:
<select id="findUserByUsername" parameterType="java.lang.String"
resultType="com.kkb.mybatis.po.User">
select * from user where username like '%${value}%'
</select>
如果参数类型是Collection的,括号中的参数名称必须是Collection;如果是List的,名称必须是list,如果是array的,名称必须是array
源码:
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap map = new StrictMap<>();
map.put(“collection”, object);
if (object instanceof List) {
map.put(“list”, object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap map = new StrictMap<>();
map.put(“array”, object);
return map;
}
return object;
}
#{}和${}区别
区别1:
#{}:相当于JDBC SQL语句中的占位符? (PreparedStatement)
${}:相当于JDBC SQL语句中的连接符合 + (Statement)
区别2:
#{}:进行输入映射的时候,会对参数进行类型解析(如果是String类型,那么SQL语句会自动加上’’)
${}:进行输入映射的时候,将参数原样输出到SQL语句中
区别3:
#{}:如果进行简单类型(String、Date、8种基本类型的包装类)的输入映射时,#{}中参数名称可以任意
:
如
果
进
行
简
单
类
型
(
S
t
r
i
n
g
、
D
a
t
e
、
8
种
基
本
类
型
的
包
装
类
)
的
输
入
映
射
时
,
{}:如果进行简单类型(String、Date、8种基本类型的包装类)的输入映射时,
:如果进行简单类型(String、Date、8种基本类型的包装类)的输入映射时,{}中参数名称必须是value
区别4:
${} :存在SQL注入问题 ,使用OR 1=1 关键字将查询条件忽略
Mybatis开发方式
1.原始dao开发方式
@Before
publicvoid init() throws Exception {
SqlSessionFactoryBuilder sessionFactoryBuilder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream(“SqlMapConfig.xml”);
sqlSessionFactory = sessionFactoryBuilder.build(inputStream);
}
@Test
publicvoid testFindUserById() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserById(22);
System.out.println(user);
}
2.mapper代理开发方式(JDK的代理方式)
2.1 代理
动态代理分为两种方式:
基于JDK的动态代理–针对有接口的类进行动态代理 ************
基于CGLIB的动态代理–通过子类继承父类的方式去进行代理。
2.2 XML方式
只需要开发Mapper接口(dao接口)和Mapper约束文件,不需要编写实现类
Mapper接口开发需要遵循以下规范:
1、 Mapper接口的类路径与Mapper.xml文件中的namespace相同。
2、 Mapper接口方法名称和Mapper.xml中定义的每个statement的id相同。
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。
4、 Mapper接口方法的返回值类型和mapper.xml中定义的每个sql的resultType的类型相同。
3. 注解方式
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
代替了 标签和标签
@Result 中 属性介绍:column 数据库的列名,Property 需要装配的属性名,one 需要使用的@One 注解(@Result(one=@One)())),many需要使用的@Many 注解(@Result(many=@many)()))
@Results:可以与@Result 一起使用,封装多个结果集
代替的是标签,该注解中可以使用单个@Result 注解,也可以使用@Result 集合,@Results({@Result(),@Result()})或@Results(@Result())
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
生命周期(作用范围)
1. sqlsession:方法级别
2. sqlsessionFactory:全局范围(应用级别)
3. sqlsessionFactoryBuilder:方法级别
全局配置文件
typeAlias标签
别名的作用:就是为了简化映射文件中parameterType和ResultType中的POJO类型名称编写
1.默认支持别名
2.自定义别名
在SqlMapConfig.xml中进行如下配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="com.kkb.mybatis.po.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name="com.kkb.mybatis.po"/>
</typeAliases>
mappers标签
1.<mapper resource=""/>
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
2.<mapper url="">
使用绝对路径加载资源
如:<mapper url="file://d:/sqlmap/User.xml" />
3.<mapper class=""/>
使用mapper接口类路径,加载映射文件
如:<mapper class="com.kkb.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中,且mapper映射文件namespace和mapper接口类路径名称相同
4.<package name=""/>
注册指定包下的所有mapper接口,来加载映射文件
如:<package name="com.kkb.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
源码
- XMLxxxBuilder是用来解析XML配置文件的,不同类型XMLxxxBuilder用来解析MyBatis配置文件的不同部位。
1、 XMLConfigBuilder用来解析MyBatis的全局配置文件
2、 XMLMapperBuilder用来解析MyBatis中的映射文件
3、 XMLStatementBuilder用来解析映射文件中的statement语句。
4、 MapperBuilderAssistant用来辅助解析映射文件并生成MappedStatement对象
映射文件
输入类型parameterType
- 包装对象:pojo类中包含pojo。
- #{}:是通过反射获取数据的
${}:是通过OGNL表达式会随着对象的嵌套而相应的发生层级变化
例:
<select id="findUserList" parameterType="queryVo" resultType="user">
SELECT * FROM user where username like '%${user.username}%'
</select>
输出类型
- resultType
resultType属性可以映射的java类型有:简单类型、POJO类型、Map类型
使用resultType进行输出映射时,要求sql语句中查询的列名和要映射的pojo的属性名一致 - resultMap
如果sql查询列名和pojo的属性名不一致,可以通过resultMap将列名和属性名作一个对应关系,最终将查询结果映射到指定的pojo对象中。
注意:resultType底层也是通过resultMap完成映射的
例:
将以下sql的查询结果进行映射:
SELECT id id_,username username_,birthday birthday_ FROM user
<!-- 定义resultMap:将查询的列名和映射的pojo的属性名做一个对应关系 -->
<!--
type:指定查询结果要映射的pojo的类型
id:指定resultMap的唯一标示,此属性表示查询结果集的唯一标识,非常重要。如果是多个字段为复合唯一约束则定义多个
-->
<resultMap type="user" id="userListResultMap">
<!--
id标签:映射查询结果的唯一列(主键列)
column:查询sql的列名
property:映射结果的属性名
-->
<id column="id_" property="id"/>
<!-- result标签:映射查询结果的普通列 -->
<result column="username_" property="username"/>
<result column="birthday_" property="birthday"/>
</resultMap>
<!-- resultMap入门 -->
<select id="findUserListResultMap" resultMap="userListResultMap">
SELECT id id_,username username_,birthday birthday_ FROM user
</select>
使用resultMap进行结果映射时,具体是使用resultMap的子标签association(一对一)和collection(一对多)完成关联查询的映射,将关联查询信息映射到pojo对象中
resultMap完成结果映射的方式是以[主信息]为主对象,[从信息]映射为集合或者对象,然后封装到主对象中
MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时,按照设置延迟规则推迟
MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式加载与深度延迟加载
例:
System.out.println(订单信息) – 将用户信息直接加载 --直接加载
System.out.println(订单信息.getID) – 查询数据库 --侵入式延迟加载
System.out.println(订单信息.用户信息.getID) – 查询数据库 --深度延迟加载
1.直接加载
通过对全局参数:lazyLoadingEnabled进行设置,默认就是false。
<settings>
<!-- 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
2.侵入式延迟加载
<settings>
<!-- 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 侵入式延迟加载开关 -->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
3.深度延迟加载
<settings>
<!-- 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 侵入式延迟加载开关 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
N+1问题
- 深度延迟加载的使用会提升性能。
- 如果延迟加载的表数据太多,此时会产生N+1问题,主信息加载一次算1次,而从信息是会根据主信息传递过来的条件,去查询从表多次。
动态SQL
1.if标签
<select id="findUserList" parameterType="queryVo" resultType="user">
SELECT * FROM user where 1=1
<if test="user != null">
<if test="user.username != null and user.username != ''">
AND username like '%${user.username}%'
</if>
</if>
</select>
2.where标签
上边的sql中的1=1,虽然可以保证sql语句的完整性:但是存在性能问题。Mybatis提供where标签解决该问题。
代码修改如下:
<select id="findUserList" parameterType="queryVo" resultType="user">
SELECT * FROM user
<!-- where标签会处理它后面的第一个and -->
<where>
<if test="user != null">
<if test="user.username != null and user.username != ''">
AND username like '%${user.username}%'
</if>
</if>
</where>
</select>
3.sql片段
在映射文件中可使用sql标签将重复的sql提取出来,然后使用include标签引用即可,最终达到sql重用的目的,具体实现如下:
- 原映射文件中的代码:
<select id="findUserList" parameterType="queryVo" resultType="user">
SELECT * FROM user
<!-- where标签会处理它后面的第一个and -->
<where>
<if test="user != null">
<if test="user.username != null and user.username != ''">
AND username like '%${user.username}%'
</if>
</if>
</where>
</select>
- 将where条件抽取出来:
<sql id="query_user_where">
<if test="user != null">
<if test="user.username != null and user.username != ''">
AND username like '%${user.username}%'
</if>
</if>
</sql>
- 使用include引用:
<!-- 使用包装类型查询用户 使用ognl从对象中取属性值,如果是包装对象可以使用.操作符来取内容部的属性 -->
<select id="findUserList" parameterType="queryVo" resultType="user">
SELECT * FROM user
<!-- where标签会处理它后面的第一个and -->
<where>
<include refid="query_user_where"></include>
</where>
</select>
注意:
1、如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:<include refid="namespace.sql片段”/>
2.foreach
<if test="ids != null and ids.size() > 0">
<!-- collection:指定输入的集合参数的参数名称 -->
<!-- item:声明集合参数中的元素变量名 -->
<!-- open:集合遍历时,需要拼接到遍历sql语句的前面 -->
<!-- close:集合遍历时,需要拼接到遍历sql语句的后面 -->
<!-- separator:集合遍历时,需要拼接到遍历sql语句之间的分隔符号 -->
<foreach collection="ids" item="id" open=" AND id IN ( "
close=" ) " separator=",">
#{id}
</foreach>
</if>
注意:如果parameterType不是POJO类型,而是List或者Array的话,那么foreach语句中,collection属性值需要固定写死为list或者array
缓存介绍
- Mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能。
- Mybatis的查询缓存总共有两级,我们称之为一级缓存和二级缓存,如图: