Mybatis入门:
Mybatis是一个非常优秀的持久层挂架(类似于Spring Data JPA).它内部封装了通过JDBC访问数据库的操作,支持定制化SQL,存储过程,高级映射
在IDEA中搭建MyBatis环境:
1.创建项目
2.在maven中导入mybatis所依赖的jar包
<!--Mybatis依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
3.创建MyBatis的核心配置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">
<!--在Mybatis的核心配置文件中是有先后顺序的-->
<configuration>
<!--指定数据库链接信息的位置-->
<properties resource="jdbc.properties" />
<!--自定义别名-->
<typeAliases>
<!--
typeAlias:定义单个别名
package:自定义扫描包里面的pojo实体类,定义别名,默认别名(首字母小写)
-->
<!--<typeAlias alias="Book" type="com.mybatis.pojo.Book" />-->
<package name="com.mybatis.pojo" />
</typeAliases>
<!--应用环境们 default默认-->
<environments default="development">
<!--应用环境-->
<environment id="development">
<!--事务管理器-->
<transactionManager type="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>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--包扫描映射
规则要求:要求接口文件名和映射文件名相同
在包扫描中,需要在resources目录中创建同名同目录结构的文件,这样才能扫描映射
-->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>
4.创建数据库配置properties文件,实体类,mapper接口和接口SQL映射的XML文件
###连接的驱动
mysql.driverClass=com.mysql.cj.jdbc.Driver
###连接的数据库
mysql.url=jdbc:mysql://localhost:3306/servlet_zhenai?useSSL=true&serverTimezone=UTC
###用户名
mysql.username=root
###密码
mysql.password=root
package com.mybatis.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @program: vip03SSM
* @description: 实体类
* @author: 高天乐
* @create: 2020-08-15 19:43
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private Integer id;
private String author;
private String name;
private String createTime;
private Double price;
private Integer sales;
private Integer stock;
}
package com.mybatis.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;
/**
* @program: vip03SSM
* @description: Book接口
* @author: 高天乐
* @create: 2020-08-15 19:52
**/
public interface BookMapper {
//查询全部
List findAll();
}
<?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="findAll" resultType="book">
select * from web_book
</select>
</mapper>
5.进行单元测试
MyBatis核心配置参数的设置、参数绑定
在Mybatis的核心配置文件中是有先后顺序的
<properties resource="jdbc.properties" /> 指定数据库连接信息的位置
resource:写数据库properties的配置文件
自定义别名
<typeAliases>
<!--
typeAlias:定义单个别名
type:里对应实体类的全限定名
alias:自定义别名不区分大小写
-->
<!--<typeAlias alias="Book" type="com.mybatis.pojo.Book" />-->
<!--
package:定义多个别名.自定义扫描包里面的pojo实体类,定义别名,默认别名(首字母小写)
name:写实体类包所在的地址
-->
<package name="com.mybatis.pojo" />
</typeAliases>
<environments default="development"> 应用环境们 default为默认环境 包含environment
<environment id="development"> 应用环境 id为唯一标识
<transactionManager type="JDBC"/> 事务管理器
<dataSource type="POOLED"> 数据库连接池 type里面可写其他的数据源 如Druid
<!--引入映射文件-->
<mappers>
<!--mapper 映射单个文件-->
<!--<mapper resource="" /> resource里写SQL映射文件的地址 -->
<!--映射过个文件需要 package标签
包扫描映射
规则要求:要求接口文件名和映射文件名相同
在包扫描中,需要在resources目录中创建同名同目录结构的文件,这样才能扫描映射
-->
<package name="com.mybatis.mapper"/>
</mappers>
动态mapper
<mapper namespace="com.mybatis.mapper.BookMapper">
namespace:传递接口的映射(命名空间,对应接口类的全限定名)
mapper里的四种子节点标签 | 作用 |
---|---|
select标签 | 对应SQL语句的查找 |
update标签 | 对应SQL语句的更新 |
delete标签 | 对应SQL语句的删除 |
insert标签 | 对应SQL语句的添加 |
上面四种标签里的属性的作用
属性 | 作用 |
---|---|
id | 唯一表示.要和接口方法名对应 |
parameterType | 方法中参数的传递类型(多个不同类型的参数,可以不写) |
resultType | 方法返回值的类型(没有返回值可以不写) |
useCache | false:禁用二级缓存(对于变化频率较高的SQL可以使用)…默认开启 |
flushCache | true:刷新缓存,默认就是true |
resultMap | 下列单独介绍 |
resultMap属性
指定字段与属性的对应映射关系 (当数据库中的字段和实体类中的属性映射不上时,通过resultMap来进行一一映射)
<resultMap id="baseResultMap" type="book">
id:自定义唯一标识,此id值用于对select元素resultMap属性的引用
type:表示该resultMap的映射结果类型(实体类,如果没有定义别名就写类全限定名)
resultMap里的子节点标签:
id标签:id标签只定义数据库表里的主键字段
result标签:对应数据库表里的字段
association标签:主要用于一对一的关联映射操作 映射到实体类的某个 "复杂类型"属性
collection标签:用来进行一对多的映射
总结一句话: resultMap标签中的子标记有先后顺序(将关联查询到的多条记录映射到集合中)
id标签 result标签 association标签的属性 | 属性介绍 |
---|---|
id标签 | column属性:对应数据库表字段 property属性:指定数据表字段名相对应的实体类属性名 |
result标签 | column属性:对应数据库表字段 property属性:指定数据表字段名相对应的实体类属性名 |
association标签 | column属性:对应数据库表字段 property属性:指定数据表字段名相对应的实体类属性名 javaType属性:属性的类型 对应类的全限定名或别名 (association标签里的子节点result标签和上列一样) |
collection | property属性:指定数据表字段名相对应的实体类属性名 ofType:指定映射的集合属性中pojo的类型 |
insert操作时主键自增:
insert中的属性 | 作用 |
---|---|
keyColumn | 指定数据库表主键的字段 |
keyProperty | 指定数据库表字段对应的实体类属性名 |
userGeneratedKeys | true开启主键回写(自增) |
<!--添加一条数据-->
<insert id="save" parameterType="book" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into web_book(author,createTime,name,price,sales,stock) values (
#{author},#{createTime},#{name},#{price},#{sales},#{stock}
)
</insert>
oracle不支持主键自增需要手动实现
<selectKey keyProperty="id" resultType="integer" order="AFTER">
select last_insert_id()
</selectKey>
selectKey:返回主键值
keyProperty:pojo类中主键的属性名
resultType:pojo类中主键属性的类型
order:生成策略,指selectKey什么时候生成,AFTER表示之后生成
MySQL的自增原理:执行完SQL语句之后才生成
多参数查询:
方法一: 根据作者和价格来进行查询默认实现
接口方法
List<T> findByAuthorAndPrice(String author ,Double price);
映射文件:
根据作者和价格来进行查询根据默认实现
<select id="findByAuthorAndPrice" resultType="book">
select * from web_book where author=#{param1} and price=#{param2}
</select>
方法一:
接口方法:根据作者和价格来进行查询注解实现
List<T> findByAuthorAndPrice(@Param("author") String author , @Param("price") Double price);
映射文件: 要注意–#{author}里的参数要和@Param(“author”)注解定义的参数写的一样
<!--根据作者和价格来进行查询根据注解实现-->
<select id="findByAuthorAndPrice" resultType="book">
select * from web_book where author=#{author} and price=#{price}
</select>
模糊查询的两种方式
方式一: 使用${}拼接SQL语句
<select id="findByLikeName" parameterType="string" resultType="student" >
select * from student where user_name like '%${username}%'
</select>
方式二:使用concat()
<select id="findByLikeName" parameterType="string" resultType="student" >
select * from student where user_name like concat('%',#{username},'%')
</select>
SQL语句里的两种传值方式
${},#{}的区别 | |
---|---|
#{} | 表示占位符,会以?号的形式显示,防止SQL注入 |
${} | 表示SQL拼接语句,会直接显示出内容.(不安全) |
提议 | #{}可以防止SQL注入,尽量少用${} |
区间查询:
在查询时有需求进行区间查询或在查询价格时查询比指定价格低或高的数据.在这里是不能直接是用 > < 号的.可以按以下进行书写
符号 | 在Mybatis SQl语句中的写法 |
---|---|
> | > |
< | < |
>= 或 <= | <![CDATA[>=]]> and <![CDATA[<=]]> |
<!--根据价格排序查询-->
<select id="findByPriceOderBy" parameterType="double" resultType="book">
<!--
在映射文件中进行范围的查询操作:>,<是使用转译符号来表示 >:> <:<
<![CDATA[符号]]> 定义
-->
select * from web_book where price < #{price} order by price asc
</select>
动态SQL
1.标签 判断语句
<!--if标签的应用-->
<select id="findByStu" parameterType="string" resultType="student">
<!--select * from Student where user_name=#{arg0} and password=#{arg1}-->
select * from Student where 1=1
<if test="userName != null and userName != ''">
and user_name=#{userName}
</if>
<if test="password != null and password != ''">
and password=#{password}
</if>
</select>
2. 辅助标签,主要用于SQL的拼接
where标签作用: 解决where 1=1 的问题
如果where标签后面的字符串是以and和or开头的就移除它
<select id="findByStu1" parameterType="string" resultType="student">
<!--select * from Student where user_name=#{arg0} and password=#{arg1}-->
select * from Student
<where>
<if test="userName != null and userName != ''">
and user_name=#{userName}
</if>
<if test="password != null and password != ''">
and password=#{password}
</if>
</where>
</select>
set标签: update student set user_name=?,password=?, where id=? 去除update拼接语句后面的最后一个逗号
<update id="updateByStu" 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>
trim标签: where标签和set标签的功能都可以使用trim标签替代
trim里的属性:
prefix: 添加前缀
suffix: 添加后缀
prefixOverrides:去除前缀
suffixOverrides:去出后缀
<update id="updateByStu1" parameterType="student">
update Student
<trim prefix="set" suffixOverrides=",">
<if test="user_name != null and user_name != ''">
user_name=#{user_name},
</if>
<if test="password != null and password != ''">
password=#{password},
</if>
</trim>
where id=#{id}
</update>
3.类似java中的switch语句,用于多条件分支判断
多条件匹配的逻辑就要使用多分支判断标记
特征:
choose标签包裹when,otherwise标签
注意:choose标签中至少要有一个when标签,0个或1个otherwise标签 (当所有的when都不满足条件时,执行otherwise)
需求: 查询操作
1.当id有值时进行id查询
2.当id没有值是进行用户名查询
3.当id和用户名都没有值是,查询无结果
<select id="findByIdOrNameStu" resultType="student">
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>
4. 循环语句(重点掌握)
collection:必填属性,值为迭代循环的属性名,传递的参数类型首字母小写
传递参数为Map时,collection里填 _parameter,或者使用注解@Param()指定别名
列表和数组有默认的别名(list/array)
item:变量名,值为迭代对象中取出的每一个值
index:索引,如果是Map类型时,这个值为Map的key值
open:循环开始的字符串
close:循环结束的字符串
separator:循环的分割符
<!--多个id查询-->
<select id="selectByIds" parameterType="integer" resultType="student">
select * from student where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
<!--多个id删除-->
<delete id="deleteByIds" parameterType="integer">
delete from student where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
<!--更新数据-->
<update id="updateByMap">
update Student
<set>
<foreach collection="_parameter" index="key" separator="," item="val">
${key} = #{val}
</foreach>
</set>
where id = #{id}
</update>
5.用于构建变量
其主要用于模糊查询当中
<select id="findByLikeUserName" parameterType="string" resultType="student">
select * from student
<where>
<if test="username != null and username != ''">
<bind name="name" value="'%'+username+'%'" />
and user_name like #{name}
</if>
</where>
</select>
关联操作
(resultMap里的属性和子节点的属性查看上文)
一对一:
理解:在任意一方添加对方的主键作为外键
<resultMap id="baseResult" type="orders">
<result column="id" property="id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!--一对一-->
<association property="user" column="user_id" javaType="user">
<result column="username" property="username" />
<result column="address" property="address" />
</association>
</resultMap>
<!--一对一查询-->
<select id="selectFindByIdOrder" parameterType="integer" resultMap="baseResult">
<!--select * from orders where id = #{id}-->
select o.*,u.username,u.address from orders o
inner join t_user u
on o.user_id = u.id
and o.id = #{id}
</select>
一对多:
理解:在"多"的一方添加"一"的一方的主键作为外键
<resultMap id="baseResult" type="orders">
<result column="id" property="id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!--一对一-->
<association property="user" column="user_id" javaType="user">
<result column="username" property="username" />
<result column="address" property="address" />
</association>
<!--一对多-->
<collection property="orderDetails" ofType="orderDetail">
<result property="items_num" column="items_num" />
<result property="items_id" column="items_id" />
</collection>
</resultMap>
<!--多对多查询-->
<select id="selectOrdersAndOrderDetail" parameterType="integer" resultMap="baseResult">
<!--
select o.*,d.items_id,d.items_num,u.username,u.address
from orders as o, orderdetail as d, t_user as u
where o.id=d.orders_id
and o.user_id = u.id
and o.id = #{id}
-->
select o.*,d.items_id,d.items_num,u.username,u.address
from (orders o inner join orderdetail d on o.id = d.orders_id)
inner join t_user u
on u.id = o.user_id
and o.id = #{id}
</select>
多对多:
1:n ---- user:orders
1:n ---- orders: orderDetail
1:1 ---- orderDetail:items
n:n ---- user:items
<resultMap id="userResult" type="user">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="address" column="address" />
<collection property="orders" ofType="orders" >
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<collection property="orderDetails" ofType="orderDetail">
<result property="items_id" column="items_id"/>
<result property="items_num" column="items_num" />
<association property="items" javaType="items" >
<result property="itemsName" column="itemsname" />
</association>
</collection>
</collection>
</resultMap>
<select id="selectByIdOrdersAndTimes" parameterType="integer" resultMap="userResult" >
<!--
select o.*,d.items_id,d.items_num,d.orders_id,u.username,u.address,i.itemsname
from orders as o, orderdetail as d, t_user as u,items as i
where o.id=d.orders_id
and o.user_id = u.id
and d.items_id = i.id
and u.id = #{id}
-->
select o.*,d.items_id,d.items_num,d.orders_id,u.username,u.address,i.itemsname
from ((orders as o inner join orderdetail as d on o.id=d.orders_id)
inner join t_user as u on o.user_id = u.id)
inner join items as i
on d.items_id = i.id
and u.id = #{id}
</select>
Mybatis的缓存
sqlSession.clearCahe(); //强制清除缓存
缓存中的数据(内存中)
缓存的作用:
从缓存中查询,减少了服务器的请求次数,提高了查询的效率,解决了高并发系统的性能问题.
Mybatis的缓存:一级缓存/二级缓存
一级缓存:
会话技术:cookie(客户端) / session(服务端)
mybatis的一级缓存的作用域是session(会话),
myBatis默认开启了一级缓存操作,并且是无法关闭的
执行过程:
Mybatis执行查询时,首先去缓存去查找,如果能找到数据则直接返回,如果没有则执行SQL语句从数据库中查询
二级缓存:
Mybatis的二级缓存是Mapper级别的缓存,不同的Mapper都有一个二级缓存,不同的Mapper之间的二级缓存是互不影响的
问题:
Mybatis是如何区分不同的Mapper的二级缓存区域?
namespace(命名空间),两个Mapper相同的话存在相同的二级缓存区域里面
开启二级缓存:在MyBatis核心配置中开启
<settings>
<!--开启二级缓存,全局开关,这里如果是关闭状态,则在Mapper中开启也没用-->
<setting name="cacheEnabled" value="true"/>
</settings>
注意:
Mybatis二级缓存需要将查询结果映射的pojo实现序列化(Serializable)
实例类序列化的目的???
序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。
简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象
在开发过程中推荐:
Redis / Ehcache
Mybatis的分页插件(pageHelper)
在核心配置中配置
<plugins>
<!--Mybatis的分页配置-->
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
/**
* 分页测试
*/
@Test
public void findPageTest(){
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
//PageHelper.startPage()配置分页 注意:需要先指定分页的条件,在执行查询
PageHelper.startPage(2, 10);
//查询数据
List<Book> bookList = mapper.findPage();
PageInfo<Book> pageInfo = new PageInfo(bookList,10);
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("当前导航页码数:" + pageInfo.getNavigatePages()); // ==10,自定义了10,默认为8
List<Book> pageList = pageInfo.getList();
for (Book book : pageList){
System.out.println(book);
}
}
以上为个人笔记总结,有错误或有待添加的地方请多多点评