一.mybatis基本组件:
1.mybatis特点:
1).支持自定义sql,存储过程,高级映射
2).实现自动对sql进行参数设置,并且能自动对结果集进行解析和封装
3).通过注解或者xml进行映射,大大减少代码量
4).数据源信息通过 配置文件进行配置
2.基本组件:
2.1 配置文件:
全局配置文件mybatis-config.xml 作用:配置数据源,引入资源文件,配置不同环境,以及引入其他映射文件等等。
mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序
2.1.1 properties属性 读取外部资源文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis
username=root
password=root
<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
<properties resource="jdbc.properties" />
通过properties引入外部文件之后,就可以通过${xxx}读取文件了
2.1.2 setting属性配置:
驼峰匹配:经典的数据库命名到经典的java中命名,就相当于去掉数据库中名字的下斜杠"_",与java匹配及第二个单词首字母大写
问题:在mybatis-config.xml写sal的时候,发现java对象与数据库列名不一样怎么解决?
1.开启驼峰命名自动匹配 如数据库 user_name java:userName .
2.使用别名替代
<settings>
<!-- 开启驼峰匹配:经典的数据库列名(多个单词下划线连接)映射到经典的java属性名(多个单词驼峰连接) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.1.3 typeAliases
类型别名是为Java类型命名一个比较短的名字。它之和xml配置有关,存在的意义仅限于用来减少类完全限定名的冗余。
有两种方式可供选择:
<typeAliases>
<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
<!-- <typeAlias type="cn.itcast.mybatis.pojo.User" alias="user" /> -->
<!-- 开启别名包扫描,name:包路径,扫描的别名就是类名,并且大小写不敏感 -->
<package name="cn.itcast.mybatis.pojo"/>
</typeAliases>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部资源文件,resource:classpath路径,url:绝对路径(不建议使用) -->
<properties resource="jdbc.properties"></properties>
<settings>
<!-- 开启驼峰匹配:经典的数据库列名(多个单词下划线连接)映射到经典的java属性名(多个单词驼峰连接) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
**<typeAliases>
<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
<typeAlias type="cn.study.mybatis.pojo.User" alias="user" />
</typeAliases>**
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml" />
<mapper resource="UserDaoMapper.xml" />
</mappers>
</configuration>
2.1.4 typeHandlers(类型处理器)
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
2.1.5 plugins (插件又称拦截器)
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
2.1.6 environments(环境)
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。
添加一个test(测试)环境,并在default参数中指向test环境。
通过build方法的重载方法
2.1.7 Mappers
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。
1.方式一:
在mybatis-config.xml引入项目目录下的映射文件:
2.方式二:
在mybatis-config.xml配置mapper接口的全路径:
这种配置方式,在全局配置文件中配置了mapper接口的全路径,并没有配置mapper接口的映射文件的位置。如果要让mybatis找到对应的映射文件,则必须满足一定的条件或规则:
1、映射文件和mapper接口在同一个目录下
2、文件名必须一致
3.映射文件的namespace必须和mapper接口的全路径保持一致
<mappers>
<!-- <mapper resource="UserMapper.xml"/> -->
<mapper resource="UserDaoMapper.xml"/>
<mapper class="cn.study.mapper.UserMapper"/>
</mappers>
总结:
1.properties:引入外部资源文件 jdbc.properties resource:classpath路径 url:物理路径
2.settings:行为参数,mapUnderscoreToCamelCase:驼峰匹配,从经典的数据库列名到经典的java属性名的映射
3.typeAliases:类型别名, typeAlias(type:包装类的全路径,alias:别名名称) package(name:包的全路径)
4.typeHandlers:类型处理器(预处理语句接收参数时,抽取结果集时)
5.plugins:插件又称拦截器
6.environments:环境,default:指定采用那个环境 配置多个环境(id:唯一标识, transactionManager:事务管理器,type=JDBC;dataSource:数据源)
7.mappers:引入映射文件,mapper(resource:classpath路径 url:物理路径 class:mapper接口的路径(同包同名,namespace是接口的全路径)),package(name:mapper接口包的全路径)
2.2 Mapper XML 文件(映射文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.study.mapper.UserMapper">
<select id="queryUserById" resultType="cn.itcast.pojo.User">
select * from tb_user where id = #{id}
</select>
<select id="queryUserAll" resultType="user">
select * from tb_user
</select>
<insert id="insertUser" parameterType="cn.itcast.pojo.User">
INSERT INTO tb_user (
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
NOW(),
NOW()
);
</insert>
<update id="updateUser" parameterType="cn.itcast.pojo.User">
UPDATE tb_user
SET
user_name = #{userName},
password = #{password},
name = #{name},
age = #{age},
sex = #{sex},
birthday = #{birthday},
updated = NOW()
WHERE
(id = #{id});
</update>
<delete id="deleteUserById" parameterType="java.lang.Long">
delete from tb_user where id=#{id}
</delete>
</mapper>
2.2.1 parameterType传入参数
CRUD标签都有一个属性parameterType,statement通过它指定接收的参数类型。
接收参数的方式有两种:
1、#{}预编译
2、${}非预编译(直接的sql拼接,不能防止sql注入)
参数类型有三种:
1、基本数据类型
2、HashMap(使用方式和pojo类似)
3、Pojo自定义包装类型
1.$ {}用法:
场景:数据库有两个一模一样的表。历史表,当前表
查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。希望使用1个方法来完成操作。
注意:
使用$ {} 去接收参数信息,在一个参数时,默认情况下必须使用${value}获取参数值,
而#{} 只是表示占位,与参数的名字无关,如果只有一个参数,可以使用任意参数名接收参数值,会自动对应,一般会在mapper类的方法参数里面,给参数添加@Param主注解。
2.多个参数
当mapper接口要传递多个参数时,有两种传递参数的方法:
1、 默认规则获取参数{0,1,param1,param2}
2、 使用@Param注解指定参数名
3 HashMap
- 基本数据类型
- hashMap
- pojo包装类
前面已经使用了基本数据类型和pojo类型的参数,那么hashMap这种类型的参数怎么传递参数呢?
其实,它的使用方式和pojo有点类似,简单类型通过#{key}或者 k e y , 复 杂 类 型 通 过 {key},复杂类型通过 key,复杂类型通过{key.属性名}或者#{key.属性名}
2.2.2 ${}与#{}区别
#{}:预编译 编译成占位符 可以防止sql注入 不需要关注数据类型 一个参数时,默认任意参数名可以接收参数
${}:非预编译 直接的sql拼接 不可以防止sql注入 需要关注数据类型 一个参数时,默认必须是value接收参数
2.2.3 模糊查询:
1.表达式: name like"%"#{name}"%"
==> Preparing: select * from bbs_brand WHERE namelike"%"?"%"and falg=? limit 0 , 10
==>Parameters: 莲(String), 1(Integer)
这个最后生成的sql 为 where name like “%” ‘莲’ “%” 能查出来;
2.表达式: name like '%${name}%'
Preparing:select count(0) from (select * from bbs_brand WHERE name like’%莲%’ and falg=?) as total
Parameters: 1(Integer)
使用$ 进行字符串的拼接,直接将值拼接上去.
3.表达式:name like CONCAT(’%’,’${name}’,’%’)**
==> Preparing: select count(0) from (select *from bbs_brand WHERE name likeCONCAT(’%’,‘莲’,’%’) and falg=?) astotal
4.update bbs_brand set is_display=0 where id IN (${ids})**
这里只能用 ${} ,当我们传入的字符串是1,3,5,7的时候,用#只能删除id为1的品牌,其他的就不能删除了,这是因为,使用了#,就是一个占位符了,经过编译后是
where id in(?) 加入字符串后是 where id in(‘1,3,5,7’) 这种,在SQL中就只会删除一个。正确的应该是where id in(‘1’,‘3’,‘5’);
2.2.4 resultMap
- 解决列名与属性名不一致的问题:
查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名
解决方案1:在sql语句中使用别名
解决方案2:参考驼峰匹配 — mybatis-config.xml 的时候
解决方案3:resultMap自定义映射
UserMapper.xml中配置resultMap
在UserMapper.xml中使用resultMap:
2.resultMap的自动映射:
在上面讲的resultMap中,主键需要id标签设置,这杨可以提高性能,然后通过result标签配置表列名和属性名对应起来,那么问题来了,是不管列名与属性对不对应都要配置还是只用配置不对应的列名?
这个取决于resultMap中的 autoMapping属性的值:
如果为true:resultMap中没有配置的字段会自动对应.
如果为false:只会针对result中已经配置的字段进行映射。
并且resultMap会自动映射单表查询的结果集。
这里千万要注意,这里配置的是resultMap 而不是 resultType 如果写错了会报错 说找不到userMap这个类!!
2.3 动态sql
<select id="queryUserListLikeUserName" resultType="User">
select * from tb_user where sex < 2
<!-- if:判断
test:OGNL表达式或者简单的java代码
-->
<if test="userName!=null && !''.equals(userName)">
and user_name like '%' #{userName} '%'
</if>
</select>
<select id="queryUserListLikeUserNameOrAge" resultType="User">
select * from tb_user where sex=1
<!-- choose:条件选择
when:test-条件,使用方式参考if的test属性,一旦有一个成立,后续的when都不再执行
otherwise:所有的when都不成立时,才会执行
-->
<choose>
<when test="userName!=null and userName.trim()!=''">and user_name like '%' #{userName} '%'</when>
<when test="age != null">and age = #{age}</when>
<otherwise>and user_name = 'zhangsan' </otherwise>
</choose>
</select>
<select id="queryUserListLikeUserNameAndAge" resultType="User">
select * from tb_user
<!--
自动添加where关键字
有一定的纠错功能:去掉sql语句块之前多余的一个and|or
通常结合if或者choose使用
-->
<where>
<if test="userName!=null and userName.trim()!=''">user_name like '%' #{userName} '%'</if>
<if test="age!=null">and age = #{age}</if>
</where>
</select>
<update id="updateUserSelective" >
UPDATE tb_user
<!--
set自动添加set关键字
也有一定的纠错功能:自动去掉sql语句块之后多余的一个逗号
-->
<set>
<if test="userName!=null and userName.trim()!=''">user_name = #{userName},</if>
<if test="password!=null and password.trim()!=''">password = #{password},</if>
<if test="name!=null and name.trim()!=''">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex}</if>
</set>
WHERE
(id = #{id});
</update>
<select id="queryUserListByIds" resultType="User">
select * from tb_user where id in
<!--
foreach:遍历集合
collection:接收的集合参数
item:遍历的集合中的一个元素
separator:分隔符
open:以什么开始
close:以什么结束
-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
二. mysql缓存:
执行相同的sql和参数,mybatis不执行sql,而是直接从缓存中命中返回。
1.一级缓存:
在mybatis中,一级缓存是默认开启的,并且一直无法关闭,在同一个sqlSession下。
使用:sqlSession.clearCache();可以强制清除缓存
执行update、insert、delete的时候,会清空缓存
2.二级缓存:
mybatis 的二级缓存的作用域:
1、同一个mapper的namespace,同一个namespace中查询sql可以从缓存中命中。
2、跨sqlSession,不同的SqlSession可以从二级缓存中命中
怎么开启二级缓存:
1、在映射文件中,添加标签
2、在全局配置文件中,设置cacheEnabled参数,默认已开启。
注意:
由于缓存数据是在sqlSession调用close方法时,放入二级缓存的,所以第一个sqlSession必须先关闭
二级缓存的对象必须序列化,例如:User对象必须实现Serializable接口。
开启二级缓存,在映射文件(UserMapper.xml)中添加:
这里的sql只会执行第一次。
@Test
public void testCache2(){
User user1 = this.userMapper.queryUserById(1l);
System.out.println(user1);
// 注意:关闭sqlSession
sqlSession.close();
System.out.println("=================第二次查询======================");
// 重新打开一个sqlSession会话
SqlSession sqlSession2 = this.sqlSessionFactory.openSession();
// 通过sqlSession2重新实例化UserMapper
this.userMapper = sqlSession2.getMapper(UserMapper.class);
User user2 = this.userMapper.queryUserById(1l);
System.out.println(user2);
}
关闭二级缓存:
不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
在mybatis-config.xml配置中:
<settings>
<!-- 行为参数,name:参数名,value:参数值,默认为false,true:开启驼峰匹配,即从经典的数据库列名到经典的java属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 关闭二级缓存,默认是开启,false:关闭 -->
<setting name="cacheEnabled" value="false"/>
</settings>