文章目录
Mybatis
1.简介
1.数据持久化
- 持久化就是将程序的数据在持久化状态和瞬时状态转化的过程
数据库JDBC,IO文件持久化,比如生活中的食物冷藏
2.持久层
Dao,Service,Controller
-
完成持久化工作的代码块
-
层界限明显
3.为什么需要Mybatis
-
帮助数据存入数据库
-
将JDBC代码简化,框架,自动化
优点:
- sql和代码的分离,提高了可维护性
- 提供映射标签,支持对象和数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
2.第一个Mybaits程序
1.搭建环境
-
创建JDBC
-
创建Maven或gradle
-
删除src(将它变成父项目)
-
导入依赖 mysql、mybatis、junit
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2.创建一个模块(子项目)
-
编写mybatis的核心配置文件
mybatis-config.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- JDBC驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sys001?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=false&verifyServerCertificate=false&autoReconnect=true&autoReconnectForPools=true&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册! 是路径用/ -->
<mappers>
<mapper resource="mybatis.xml"/>
</mappers>
</configuration>
编写工具类Utils
3.编写代码
实体类
@Data //提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法
@Builder //为你的类生成相对略微复杂的构建器API。
@NoArgsConstructor //为类提供一个无参的构造方法
@AllArgsConstructor //为类提供一个全参的构造方法
public class SysJob implements Serializable {
@ApiModelProperty("岗位ID") //表示对model属性的说明或者数据操作更改
private String jobId;
@ApiModelProperty("岗位名称")
private String jobName;
@ApiModelProperty("岗位类型")
private String jobType;
@ApiModelProperty("岗位状态")
private String jobStatus;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("编辑时间")
private Date editTime;
@ApiModelProperty("编辑用户")
private String editUser;
private static final long serialVersionUID = 1L;
}
Dao接口(Mapper映射器)
public interface BaseMapper<T> {
int deleteByPrimaryKey(Object var1);
int insert(T var1);
int insertSelective(T var1);
int updateByPrimaryKeySelective(T var1);
int updateByPrimaryKey(T var1);
T selectByPrimaryKey(Object var1);
int selectCount(Map<String, Object> var1);
List<Map<String, Object>> selectList(Map<String, Object> var1);
int selectCount(PageDTO var1);
List<Object> selectList(PageDTO var1);
List<T> selectAllList(T var1);
List<Map<String, Object>> selectByMap(Map<String, Object> var1);
}
public interface SysJobMapper extends BaseMapper<SysJob>{
}
接口实现类xml文件(比如UserMapper)
<?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">
<!-- namespace = 绑定一个Dap/Mapper接口 -->
<mapper namespace="cn.bywin.social.dao.SysJobMapper">
<!-- sql语句 id=方法名 -->
<!-- 结果集写权限命名 resultType(单个) resultMap(集合) -->
<!-- 对象中的属性可以直接取出 -->
<select id="selectAllList" resultMap="BaseResultMap" parameterType="cn.bywin.social.model.SysJob" >
select
<include refid="Base_Column_List" />
from sys_job t
<include refid="Condition" />
</select>
</mapper>
4.增删改查
1.编写mapper接口
2.编写对应的mapper中的测试语句
-
namespace
—包名要和Dao/Mapper接口的包名一致
-
语句参数
—id: 对应的namespace中的方法名
—resultType: sql语句的返回值
—parameterTyoe: 参数类型
3.万能的Map
————————————————(多参数用)
- 不需要写全每个字段,只要字段的属性不是Not Null都可以不传入
- 是对象类(User)的话需要与对象中的属性一一对应
int selectCount(Map<String, Object> var1);
List<Map<String, Object>> selectList(Map<String, Object> var1);
<sql id="Condition">
<where>
<if test="jobName != null">
and job_name like '%${jobName}%'
</if>
<if test="jobType != null">
and job_type like '%${jobType}%'
</if>
<if test="editUser != null">
and edit_user like '%${editUser}%'
</if>
</where>
</sql>
<select id="selectList" resultType="cn.bywin.common.model.DataMap" parameterType="Map" >
select t.* from sys_job t
<!-- include是将代码块打包 -->
<include refid="Condition" />
<include refid="Pagination" />
</select>
4.模糊查询
- java代码执行和,传递通配符 $ {value} 用美元符$
- 其余用 #{value} 来传递参数
5.核心配置文件
1.环境配置(environments)
-
可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境
-
默认使用的环境 ID(比如:default=“development”)。
-
每个 environment 元素定义的环境 ID(比如:id=“development”)。
-
事务管理器的配置(比如:type=“JDBC/MANAGED” 默认是JDBC)。
-
数据源的配置(比如:type=“POOLED” 连接池)。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
2.数据源(dataSource)
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/sys001?${spring.datasource.driverParams}
username: root
password: 123456
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。url
– 这是数据库的 JDBC URL 地址。username
– 登录数据库的用户名。password
– 登录数据库的密码。
3.属性(properties)
这些属性可以在外部进行配置(引入外部配置文件),并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
<!-- 注意:每个标签必须按顺序写 properties在最上面-->
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
4.类型别名(typeAliases)
1.类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="sysjob" type="cn.bywin.social.model.SysJob"/>
</typeAliases>
<!-- parameterType="cn.bywin.social.model.SysJob" -> parameterType="SysJob" -->
<select id="selectAllList" resultMap="BaseResultMap" parameterType="SysJob" >
select
<include refid="Base_Column_List" />
from sys_job t
<include refid="Condition" />
</select>
2.也可以指定一个包名,MyBatis 会扫描包中的的 Java Bean,使用默认别名(首字母小写) 比如:
<typeAliases>
<package name="cn.bywin.social.model"/>
</typeAliases>
<!-- parameterType="cn.bywin.social.model.SysJob" -> parameterType="sysJob" -->
<select id="selectAllList" resultMap="BaseResultMap" parameterType="sysjob" >
select
<include refid="Base_Column_List" />
from sys_job t
<include refid="Condition" />
</select>
5.设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
6.映射器(Mappers)
定义SQL映射语句
方法一【推荐】:使用相对路径 resource=
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
方法二:使用class文件绑定注册 class=
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="cn.bywin.dao.UserMapper"/>
</mappers>
- 接口和它的Mapper配置文件必须同名
- 接口和它的Mapper配置文件必须在同一个包
方法三:使用扫描包进行注入绑定
<mappers>
<package name="cn.bywin"/>
</mappers>
- 接口和它的Mapper配置文件必须同名
- 接口和它的Mapper配置文件必须在同一个包
7.生命周期和作用域
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不需要它了
- 局部变量
SqlSessionFactory:
- 可以想象成:数据库连接池
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 因此 SqlSessionFactory 的最佳作用域是应用作用域
- 使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完之后需要关闭请求,否则资源被占用
6.ResultMap结果映射
解决实体类中属性名和数据库中字段名不一致的问题
- 1.定义结果集映射
<!-- 定义一种结果集 -->
<resultMap id="BaseResultMap" type="SysJob" >
<!-- column=字段名 property=属性名 -->
<id column="job_id" property="jobId" jdbcType="VARCHAR" />
<result column="job_name" property="jobName" jdbcType="VARCHAR" />
<result column="job_type" property="jobType" jdbcType="VARCHAR" />
<result column="job_status" property="jobStatus" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="edit_time" property="editTime" jdbcType="TIMESTAMP" />
<result column="edit_user" property="editUser" jdbcType="VARCHAR" />
</resultMap>
- 2.resultMap=“BaseResultMap” 对应定义的id
<select id="selectAllList" resultMap="BaseResultMap" parameterType="SysJob" >
select
<include refid="Base_Column_List" />
from sys_job t
<include refid="Condition" />
</select>
-
resultMap
元素是 MyBatis 中最重要最强大的元素。 -
resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
-
只需要写字段名和属性名不一样的就可
7.日志工厂
如果一个数据库操作出现了异常,我们需要用日志排错
以前:sout、debug
- SLF4J
- LOG4J 【掌握】(需要导包)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】(标准日志不需要导包)
- NO_LOGGING
在Mybatis中具体使用哪个日志实现,在设置中设定!
<settings><!-- 标准日志实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
8.分页
- 减少数据的处理量
使用Limit分页
select * form user limit startIndex,pageSize
9.注解开发
9.1、面向接口编程
- 根本原因:解耦
- 接口:定义与实现的分离
9.2、注解(简单语句)
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM User WHERE id = #{id}")
Blog selectBlog(int id);
}
10.Lombok
使用步骤
- 在IDEA中安装lombok插件(Setting->Plugins->Lombok)
- 在项目中导入Lombok包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
- 在实体类上加注解即可!
@Getter
@Setter
@ToString
@EqualsAndHashCode
@AllArgsConstructor //为类提供一个全参的构造方法
@NoArgsConstructor //为类提供一个无参的构造方法
@Data //提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法
@Builder //为你的类生成相对略微复杂的构建器API。
11.关系查询
1.多对一
嵌套结果映射 – 集合可以是 resultMap
元素,或是对其它结果映射的引用
对象:association
- 一个复杂类型的关联;许多结果将包装成这种类型
集合:collection
- 一个复杂类型的集合
- 复杂嵌套查询(子查询)
- 连表查询——按照结果嵌套查询
2.一对多
- 复杂嵌套查询(子查询)
- 连表查询——按照结果嵌套查询
3、多对多
需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
4.小结
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- javaType & ofType
- javaType 用来指定实体类中属性的类型
- ofType 用来指定映射到List或集合中的pojo类型(泛型中的约束类型)
12.动态SQL
动态SQL就是根据不同的条件生成不同的SQL语句
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
UUID生成随机的ID
1、IF
使用动态 SQL 最常见情景是根据条件包含 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>
2、choose(when、otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<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>
</where>
</select>
3、trim(where、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>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
<!-- prefix前缀 suffix后缀 prefixOverrides定义前缀 suffixOverrides定义后缀-->
insert into sys_job
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="jobId != null" >
job_id,
</if>
<if test="jobName != null" >
job_name,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="jobId != null" >
#{jobId,jdbcType=VARCHAR},
</if>
<if test="jobName != null" >
#{jobName,jdbcType=VARCHAR},
</if>
</trim>
可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素
4、Foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
<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>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。
5、SQL片段打包
1、使用SQL标签抽得公共部分
<sql id="Base_Column_List" >
job_id, job_name, job_type, job_status, create_time, edit_time, edit_user
</sql>
<!-- 在需要用到的地方使用 <include refid="" /> -->
2、在需要的地方使用include标签引用
<select id="selectAllList" resultMap="BaseResultMap" parameterType="cn.bywin.social.model.SysJob" >
select
<include refid="Base_Column_List" />
from sys_job t
<include refid="Condition" />
</select>
13、缓存
1、简介
查询 :连接数据库,要耗资源!
将一次查询的结果,给他暂存在一个可以直接得到的地方!-->内存 :缓存
当我们再次查询相同数据的时候,直接走缓存,而不用连接数据库
1、什么是缓存
- 存在内存中的临时数据
- 将用户经常查询的数据放再缓存中,再次查询走缓存,从而提高查询效率,解决了高并发系统的性能问题
2、为什么使用缓存
- 减少和数据库的交互次数,减少系统开销,提高系统效率
3、什么要的数据能使用缓存
- 经常查询并不经常改变的数据
2、Mybatis缓存
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
-
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
-
MyBatis系统中默认定义了:一级缓存和二级缓存
-
默认一级缓存开启(SqlSession级别的缓存)
-
二级缓存需要手动开启,是基于namespace级别的缓存
-
为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存
-
3、一级缓存
- 一级缓存也叫本地缓存
- 与数据库同一次会话期间查询到的数据会放再本地缓存中
4、二级缓存
- 二级缓存也叫全局缓存
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
-
工作机制
-
- 一个会话查询一条数据,这个数据放入一级缓存
- 当会话关闭,一级缓存没了后,将被保存入二级缓存
- 新的会话查询可以从二级缓存获得内容
- 不同的mapper查出的数据会放再自己对应的缓存(map)中
步骤
1.开启全局缓存
<setting name="cacheEnable" value="true"/>
2、在要使用二级缓存的Mapper中开启
<!-- 在当前Mapper.xml中使用二级缓存 -->
<cache/>
也可以自定义参数
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
5、缓存原理
6、自定义缓存(ehcache)
1、导包
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.4</version>
</dependency>
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
2、ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shiroCache">
<diskStore path="java.io.tmpdir"/>
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:
Ehcache的三种清空策略;
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="0"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 登录记录缓存锁定5分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>