Mybatis框架
学前预热
Mybatis是持久层框架(类似于Spring Data JPA)支持定制化的SQL,存储过程,
Spring Data JPA
1.基于命名规则的查询(只能进行select操作)
2.基于注解@Query方式的JPQL语句操作(可进行增删改操作)
一.Mybatis入门
1.导入依赖
<!-- slf4j日志依赖jar-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
<!--有参和无参构造注解的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--单元测时的依赖--->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13-rc-2</version>
<scope>test</scope>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
2.规则:
实体类的属性名一定要和数据库对应的字段名一致(一摸一样),不然使用动态mapper时无法相互映射;并且实体类的属性名不能为关键字,否则报错.
3.创建mybatisConfig.xml文件
<?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>
<!--指定数据库连接信息的位置-->
<properties resource="jdbc.properties"/>
<!--自定义实体类别名-->
<!--有先后顺序 properties->typeAliases->environments -->
<typeAliases>
<!--typeAlias 自定单个别名 可以写多个
<typeAlias type="com.mybatis.pojo.Book" alias="Book"/>
-->
<!--自定义扫描包里面的pojo实体类别名,别名默认为类名(首字母小写)
//自定以多个别名
-->
<package name="com.mybatis.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 使用JDBC的数据源
<dataSource type="POOLED" >
<property name="driver" value="${mysql.driverClass}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
-->
<!---使用druid连接池的方式连接数据库-->
<dataSource type="com.mybatis.config.DruidDataSourceFactory">
<property name="driverClassName" value="${mysql.driverClass}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--包映射扫描
规则要求:接口文件名和映射文件名要相同
-->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>
4.创建Mybatis的映射文件xxxMapper.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="com.mybatis.mapper.BookMapper">
<select id="selectFindById" parameterType="int" resultType="Book">
<!--业务逻辑:
#{}:表示一个占位符(?)
${}:表示一个SQL语句串,将接收到的内容不加修饰直接拼接到sql语句中 容易造成SQL注入
-->
select * from jpa_book where id=#{id};
</select>
</mapper>
id:在动态代理过程中,这个id的值唯一,且与接口中的方法名对应
resultType:接口中方法的返回值,写全路径名;当自定义单个别名时可以直接写定义好的别名;当自定义扫描包里面的pojo实体类别名时,别名默认为类名(首字母小写)
parameterType:方法的参数类型
namespace:命名空间,用来指定接口的位置,写全路径
5.构建SqlSessionFactory
Mybatis通过SqlSessionFactory来加载配置文件
public class MybatisTest {
private SqlSession sqlSession = null;
private BookMapper mapper=null;
//在测试类执行之前执行
@Before
public void before() throws IOException {
//加载配置文件
InputStream inputStream = Resources.getResourceAsStream("myBatisConfig.xml");
//获取sqlSessFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取工厂实例
sqlSession = sqlSessionFactory.openSession();
//mybatis动态代理方式的实现
mapper = sqlSession.getMapper(BookMapper.class);
}
}
二.Mybatis参数的设置
1.数据库进行添加操作需要注意的问题
①.主键自增:在建立数据库表的时候可以设置主键自增
②.主键不自增:可以自己手动设置
<insert id="save" parameterType="Book" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into jpa_book (author,createTime,name,price,sales,stock) values (#{author},#{createTime},#{name},#{price},#{sales},#{stock});
</insert>
keyColumn=“id” 指定数据库表主键字段
keyProperty=“id” 设置数据库表对应实体类的属性名
useGeneratedKeys=“true” 开启主键自增
③.主键不支持自增:例如Oracle数据库就不支持自增
<selectKey keyProperty="id" resultType="int" keyColumn="id" order="BEFORE">
select LAST_INSERT_ID <!--或者使用 select UUID()生成-->
</selectKey>
order=“BEFORE” 表示先生成主键再进行自增
select LAST_INSERT_ID 或者使用 select UUID() 通过函数生成主键值
2.实体类属性名和数据库字段名映射
当实体类中的属性与数据库表中的字段名不一致时可以通过 resultMap 将属性值与字段名互相映射.
<resultMap id="resultMap" type="com.mybatis.pojo.Book">
<id column="id" property="id"/>
<result column="author1" property="author"/>
</resultMap>
resultMap :指定与属性对应映射关系 ,此标签中的id值是一个标识,表示经过别名映射后的实体类别名.
id: 指定主键字段 该值唯一
column: 表字段
property: 实体类属性
此时可以将resultType替换成resultMap=“resultMap”
3.定义别名
在mybatisConfig.xml文件中定义实体类的别名,在用到实体类时就可以用别名来代替全路径的实体类了.
<!--typeAlias 自定单个别名 可以写多个-->
<typeAlias type="com.mybatis.pojo.Book" alias="Book"/>
<!--自定义扫描包里面的pojo实体类别名,别名默认为类名(首字母小写)
自定以多个别名
-->
<package name="com.mybatis.pojo"/>
4.范围查询操作
<!--在写映射文件时,进行范围查询操作的时候要进行符号转义
符号转义: > === > === <![CDATA[>]]> < === < === <![CDATA[<]]>
-->
<select id="findByPriceOrderBy" parameterType="double" resultType="Book">
select * from jpa_book where price > 100 and price < 200;
</select>
三.参数绑定和动态Mapper
1.多参数绑定
注意:进行多参数业务操作时,要将参数与SQL语句中的值进行绑定
<select id="login" parameterType="string" resultType="student">
select * from student where user_name = #{username} and password = #{password};
</select>
方法一:
将#{username}和#{password}改成:#{arg0}和#{arg1}进行绑定.
<select id="login" parameterType="string" resultType="student">
select * from student where user_name = #{arg0} and password = #{arg1};
</select>
方法二:
将#{username}和#{password}改成:#{param1}和#{param2}进行绑定.
<select id="login" parameterType="string" resultType="student">
select * from student where user_name = #{param1} and password = #{param2};
</select>
通过@Param()注解的方式进行绑定.
public interface StudentMapper{
Student login(@Param(value = "username") String userName, @Param(value = "password") String password);
}
2.模糊查询
<select id="findByLikeName" parameterType="string" resultType="student">
<!--模糊查询时
用$时: '%${username}%'
用#时: CONCAT('%', #{username}, '%')
-->
select * from student where user_name like CONCAT('%', #{username}, '%')
</select>
3.包映射扫描
当有多个映射文件时,为了不一一引入映射文件,这里可以通过包扫描的方式扫描所在包下面的所用映射文件.
扫描com.mybatis.mapper包下面的xxxMapper.xml
<!--引入映射文件-->
<mappers>
<!--包映射扫描-->
<package name="com.mybatis.mapper"/>
</mappers>
注意:进行包扫描的前提是接口文件名和映射文件名要相同并且接口的包路径要和映射文件的包路径一致,例如:都是com.mybatis.mapper,否者会报错
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.mybatis.mapper.StudentMapper.xxxx
4.使用druid数据源
Mybatis并没有实现druid/C3P0连接池,在使用的时候需要自己手动去实现
public class DruidDataSourceFactory extends UnpooledDataSourceFactory {
public DruidDataSourceFactory(){
this.dataSource = new DruidDataSource();
}
}
<dataSource type="com.mybatis.config.DruidDataSourceFactory">
<!--注意:这里如果使用JDBC进行连接的时候要改成name="driver"-->
<property name="driverClassName" value="${mysql.driverClass}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
四.动态SQL
Mybatis的执行流程
常用的sql标签:
1.if判断语句(只能做单分支判断 只有if没有else)
<!--if标签的运用-->
<select id="findByStu" parameterType="string" resultType="com.mybatis.pojo.Student">
<!--可实现根据username和password任何一个查询,当都为空是就查询全部-->
select * from Student
<!--
where标签的作用
解决where 1=1 的问题
如果where标签后面的字符串是以and和or开头,就移除它
-->
<where>
<!--if标签里的test参数用于判断传递的参数是否为空-->
<if test="username !=null and username !=''">
and user_name = #{username}
</if>
<if test="password !=null and password !=''">
and password = #{password}
</if>
</where>
</select>
2.choose when otherwise 类似java中的switch语句,用于多条件分支判断
特征:
choose标签包裹when,otherwise标签
choose标签中至少要有一个when标签,0个或一个otherwise标签
<select id="findByIdOrName" resultType="student">
<!--
需求:
查询操作
1.当id有值时进行id查询
2.当id没有值时进行username用户名查询
3.当id和username都没有用值时,查询无结果
-->
select * from Student
<where>
<choose>
<when test="id !=null and id !=''">
and id=#{id}
</when>
<when test="username !=null and username !=''">
and user_name =#{username}
</when>
<otherwise>
and id = -1
</otherwise>
</choose>
</where>
</select>
<update id="updateByStu1" parameterType="student">
update Student
<set>
<if test="user_name !=null and user_name !=''">
user_name = #{user_name},
</if>
<if test="password !=null and password !=''">
password = #{password}
</if>
</set>
where id=#{id}
</update>
where标签:当where标签后面的字符串是以and和or开头时,就移除移除and或则时or完成SQL的拼接
set标签:只能用于更新操作中,为了去掉where之前的逗号(,)
trim标签:where和set标签的功能都可以使用trim标签替代
prefix: 添加前缀,将prefix里的内容添加到if标签的内容前面
prefixOverrides:去除前缀,将prefixOverrides里的内容从if标签里的内容去掉
suffix:添加后缀
suffixOverrides:去除后缀
<select id="findByStu" parameterType="string" resultType="com.mybatis.pojo.Student">
select * from Student
<trim prefix="where" prefixOverrides="and">
<if test="username !=null and username !=''">
and user_name = #{username}
</if>
<if test="password !=null and password !=''">
and password = #{password}
</if>
</trim>
</select>
3.foreach 循环语句
select from student where id in(5,6,7)
1.批量查询
2.批量删除
3.动态更新(map)
<!--批量查询-->
<select id="selectById" parameterType="int" resultType="Student">
select * from Student where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
<!--批量删除-->
<delete id="deleteByIds" parameterType="boolean">
delete from Student where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
<!--批量更新-->
<update id="update">
update student
<set>
<foreach collection="_parameter" index="key" separator="," item="val">
${key}=#{val}
</foreach>
</set>
where id = #{id}
</update>
foreach包含的属性:
collection:必填,值为迭代循环的属性名,传递参数类型(首字母小写)
List(集合)-->list Array-->array Map-->_parameter或者使用@Param()指定别名
item:变量名,值为迭代循环队形中取出的每一个值
index:索引,如果时Map类型,这个值为Map的key值
open:循环开始的字符串
close:循环结束的字符串
separator:分隔符
4.bind 用于构建变量
格式: 主要用于模糊查询
'%${username}%'拼接形式进行模糊查询容易造成SQL注入
concat(’%’,username,’%’)这种方式只支持Mybatis,不支持Oracle
<select id="bind" parameterType="string" resultType="Student">
select * from student
<where>
<if test="username !=null and username !=''">
<!--也可以<bind name="name" value="'%'+username+'%'"/>-->
<bind name="name" value="'%'+_parameter+'%'"/>
and user_name like #{name}
</if>
</where>
</select>
五.关联查询
对于建模的关联关系(3种),不同类型的关系都是通过数据库表种的外键将表与表之间联系起来.
1).1:1
一个人对应一个身份证
数据库的关联方式:在任意一方引入对方的主键作为外键
2).1:n
一个班级有多个学生
再"多"的一方添加"一"的一方的主键作为外键
3).n:m
学生和课程
多对多的关系会重新创建一个新的中间表,中间表引入两个表的主键作为外键
<resultMap id="baseResultMap" type="orders">
<id column="id" property="id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<association property="user" column="user_id" javaType="user">
<result column="username" property="username"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="selectFindById" parameterType="integer" resultMap="baseResultMap">
<!--SQL中orders和u_user表关联-->
select o.*,u.* from orders as o,u_user as u
where o.user_id = u.id and o.id=#{id}
</select>
1.association标签
association: association是resultMap的子标签,主要用于一对一的关联映射操作,如上:将实体类User与orders表中作为user表的外键的user_id进行映射
property:对应实例中的属性名
column:数据库表字段(orders表中的u_user表的外键user_id)
javaType:属性的类型(一般情况下是对象类型[实体类的首字母小写])
2.collection标签
collection:它也是resultMap的子标签,主要用于一对多的操作
property:映射到orders类中的属性名
ofType:指定映射集合属性中的实体类类型
注意:在关联查询中必需要使用resultMap
pojo中的实体类要进行序列化(Serializable)
实体类中序列化的作用: 序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。
简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象
3.关联查询中JOIN的用法
六.Mybatis缓存
1.Mybatis为什么要产生缓存?
当进行数据库操作时,程序会产生缓存,并且将缓存的数据存储在内存中;当再次执行同样的操作时,程序会首先从缓存中进行查询,这样可以减少服务器的请求次数,也提高了查询的效率,能够解决高并发系统的性能问题.
2.Mybatis的缓存
2.1.一级缓存
Mybatis的一级缓存的作用域是session,可以通过sqlSession.clearCache()在程序中清除缓存.
默认是开启了一级缓存操作,并且是无法关闭的.
执行过程: Mybatis执行查询语句时首先会去缓冲区查找,如果能找到则直接返回,如果没有则执行SQL语句从数据库中查询.
2.2.二级缓存
Mybatis的二级缓存是Mapper级别的缓存,每一个命名空间(namespace)都有一个二级缓存,不同的命名空间的二级缓存是互不影响的.
开启二级缓存: 在mybatis配置文件中添加settings标签来设置,缓存默认情况是开启的;如果在这里关闭了,在mapper配置文件中开启是无效的.
<settings>
<setting name = "cacheEnabled" value = "true" />
</settings>
注意: 开启Mybatis二级缓存需要将查询结果映射的pojo实现序列化(Serializable)
业务中禁用二级缓存: select标签中userCache="false"用来禁用二级缓存,对于变化频率较高的的SQL操作可以禁用.
刷新缓冲区: 在select标签中添加flaushCatch="true"可以自动刷新缓存,默认是true
七.Mybatis分页插件
导入依赖
<!--Mybatis分页插件的依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
在Mybatis的配置文件configuration标签里添加分页配置
<!--Mybatis的分页配置-->
<plugins>
<!--5版本的用PageInterceptor-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
分页测试类:
//分页测试
@Test
public void testPage(){
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
//注意:需要先指定分页的条件,在执行查询
PageHelper.startPage(2, 10);
List<Book> bookList = bookMapper.findPage();
PageInfo pageInfo = new PageInfo(bookList);
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("总页数: "+pageInfo.getPages());
System.out.println("当前页: "+pageInfo.getPageNum());
List<Book> pageList=pageInfo.getList();
for (Book book:pageList){
System.out.println(book.toString());
}
}
MyBatis Helper PageInfo属性详解:
PageInfo
{pageNum=1,当前页码
pageSize=1,每页个数
size=1,当前页个数
startRow=1,由第几条开始
endRow=1,到第几条结束
total=3,总条数
pages=3,总页数
list=Page{count=true, pageNum=1, pageSize=1, startRow=0, endRow=1, total=3,
pages=3, reasonable=false, pageSizeZero=false},当前页数据的结果集
prePage=0,上一页
nextPage=2,下一页
isFirstPage=true,是否为首页
isLastPage=false,是否为尾页
hasPreviousPage=false,是否有上一页
hasNextPage=true,是否有下一页
navigatePages=8,每页显示的页码个数
navigateFirstPage=1,首页
navigateLastPage=3,尾页
navigatepageNums=[1, 2, 3]}页码数