一、MyBatis简介
1、历史
- MyBatis 本是 apache 的一个开源项目 iBatis , 2010年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。
- iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
2、作用
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及对结果集的检索封装。
- MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
3、持久化
持久化就是把数据存在磁盘中,而不是内存中
-
程序产生的数据都是在内存中的
-
内存 一旦断电 数据就会丢失
-
磁盘中存储数据的地方:数据库文件,xml文件,任意可以读取数据的文件
-
持久化的原因是:内存太贵,对象数据很珍贵(不想丢弃)
高级的说法:持久化就是将数据从瞬时态转化为持久态,长久保存。
4、持久层
- 业务需要操作数据,但是数据储存在磁盘
- 具体业务调用具体的数据库操作,耦合度高,复用性差
- 将操作数据库的代码抽离出来,形成了介于业务层和数据库中间的独立层次
5、持久层框架
- 传统的JDBC代码过于复杂,重复的代码太多。简化–>框架–>自动化
- 利用框架可以帮助实现持久层的操作,避免重复而且复杂的操作
- 实现了实体类与数据库映射的关系,即 ORM 映射(对象关系映射Object Relational Mapping,简称ORM)
6、MyBatis的优缺点
- sql语句与代码分离,存放在xml文件中
- 优点:便于维护,不需要再java代码中寻找sql语句
- 缺点:无法通过断点进行调试,需要log4j输出日志信息进行调试修改
- 用逻辑标签控制动态SQL的拼接
- 优点:用标签代替代码编写
- 缺点:不够灵活,拼写复杂
- 查询的结果集与java对象自动映射
- 优点:保证名称相同,配置好映射关系即可自动映射或者,不配置映射关系,通过配置列名=字段名也可完成自动映射。
- 缺点:对SQL依赖性强
- 编写原生的SQL
- 优点:接近JDBC,灵活
- 缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要调整。
- 使用的人数多,但是由于使用了反射,所以使得效率下降。
二、环境搭建
1、构建父工程(尝试学习聚合工程规
范)
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm-study</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-study</module>
</modules>
<packaging>pom</packaging>
2、父工程的maven配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm-study</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-study</module>
</modules>
<packaging>pom</packaging>
<!-- 父模块用于约束版本信息 -->
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
<junit.version>4.12</junit.version>
<mybatis.version>3.5.3</mybatis.version>
<mysql-connector-java.version>8.0.23</mysql-connector-java.version>
<lombok.version>1.18.18</lombok.version>
<hikari.version>2.1</hikari.version>
<log4j.version>1.2.12</log4j.version>
<slf4j.version>1.5.6</slf4j.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- mybatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- 数据库确定 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>runtime</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<dependency>
<groupId>dev.tuxjsql</groupId>
<artifactId>hikaricp-cp</artifactId>
<version>${hikari.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
3、构建子模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm-study</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatis-study</artifactId>
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>dev.tuxjsql</groupId>
<artifactId>hikaricp-cp</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
</dependencies>
<!-- 处理资源被过滤问题 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<target>14</target><!-- 源代码使用的JDK版本 -->
<source>14</source><!-- 需要生成的目标class文件的编译版本 -->
<encoding>utf-8</encoding><!-- 字符集编码 -->
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
4、MyBatis核心配置文件:mybatis-config.xml
Tips:
-
DTD(Document Type Definition)文档类型定义,是一种XML约束模式语言,是XML文件的验证机制
-
DTD文档包括:
- 元素的定义规则
- 元素间的定义规则
- 元素可用的属性、实体、符号规则
-
dataSource type
-
UNPOOLED:不使用连接池的数据源
-
POOLED:使用连接池的数据源
-
JNDI:使用JNDI实现的数据源
-
此处使用的自己定义的数据源:
<properties resource="db.properties" /> <dataSource type="com.assin.datasource.HikariDataSource"> <property name="driverClassName" value="${driver}"/> <property name="jdbcUrl" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
property中value值在db.properties文件中,${} 可以获取文件中的数据
-
<?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="db.properties" />
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.assin.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="com.assin.datasource.HikariDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
自定义的数据源:com.assin.datasource.HikariDataSource
package com.assin.datasource;
import com.zaxxer.hikari.HikariConfig;
import org.apache.ibatis.datasource.DataSourceFactory;
import javax.sql.DataSource;
import java.util.Properties;
/**
* @Author:ASSIN
* @Date: 2021/9/7 13:25
*/
public class HikariDataSource implements DataSourceFactory {
Properties properties = null;
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public DataSource getDataSource() {
HikariConfig hikariConfig = new HikariConfig(properties);
return new com.zaxxer.hikari.HikariDataSource(hikariConfig);
}
}
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
username=root
password=root
5、实体类(entity)
偷懒神器:Lombok
在编译的时候动态地自动生成getter、setter、toString等方法
-
安装插件 Lombo
-
引入lombok的依赖
编译的的时候,编译的时候用,运行的时候不用
scope 选择 provided
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
-
使用:
在对应的类加上注解
- @AllArgsConstructor:生成全参构造器。
- @NoArgsConstructor:生成无参构造器。
- @Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。可以设定访问权限及是否懒加载等。
- @Data:作用于类上,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @Setter
- @Log:作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解。
注解还有很多,这里仅仅列举了几个
注意Lombok的版本,遇到版本太低,导致注解失效
package com.assin.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* @Author:ASSIN
* @Date: 2021/9/5 22:28
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class User implements Serializable {
private int id;
private String username;
private String password;
}
三、CRUD
需要注意的两个文件
- 一个接口类mapper(就是咱们写的dao)java文件,不需要有实现
- 一个与接口对应的xml文件
- 两个文件名字要一样,比如一个UserMapper.java一个UserMapper.xml
- 框架要根据mapper和xml联合,形成 代理对象
接口类:XXXDao(mapper)
userDao
package com.assin.dao;
import com.assin.entity.User;
/**
* @Author:ASSIN
* @Date: 2021/9/5 22:43
*/
public interface UserDao {
/**
* 根据 id 查找用户
* @param id
* @return
*/
User selectUserById(int id);
/**
* 保存用户
* @param user
* @return
*/
int saveUser(User user);
/**
* 更新用户
* @param user
* @return
*/
int updateUser(User user);
/**
* 根据id删除用户
* @param id
* @return
*/
int deleteUserById(int id);
}
XXXMapper.xml 的格式
userMapper.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.assin.dao.UserDao">
<select id="selectUserById" parameterType="int" resultType="user" >
select * from user where id = #{id}
</select>
<insert id="saveUser" parameterType="user">
insert into user (id,username,password) values (#{id},#{username},#{password})
</insert>
<update id="updateUser" parameterType="user">
update user set username = #{username},password=#{password} where id=#{id}
</update>
<delete id="deleteUserById" parameterType="integer">
delete from user where id=#{id}
</delete>
</mapper>
xxxMapper.xml 需要在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>
<mappers>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
四、使用注解开发
- mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了 新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映 射并不能用注解来构建
- sql 类型主要分成 :
- @select ()
- @update()
- @insert()
- @delete()
注意:利用注解开发就不需要mapper.xml映射文件了 .
1、在接口中添加注解
@Select("select id,username,password from user where id=#{id}")
Admin findUserById(@Param("id") int id);
2、核心配置文件中配置
<mappers>
<mapper class="com.assin.dao.UserMapper"/>
</mappers>
五、动态sql
1、简介
MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以 达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。
动态SQL元素
2、if元素
if元素相当于Java中的if语句,它常常与test属性联合使用。现在我们要根据name去查找学生,但是 name是可选的,如下所示:
<select id="findUserById" resultType="com.xinzhi.entity.User">
select id,username,password from user
where 1 =1
<if test="id != null and id != ''">
AND id = #{id}
</if>
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="password != null and password != ''">
AND password = #{password}
</if>
</select
3、choose、when、otherwise元素
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器 的动态语句中可以使用choose、when、otherwise元素。
<!-- 有name的时候使用name搜索,没有的时候使用id搜索 -->
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
WHERE 1=1
<choose>
<when test="name != null and name != ''">
AND username LIKE concat('%', #{username}, '%')
</when>
<when test="id != null">
AND id = #{id}
</when>
</choose>
</select>
4、where元素
上面的select语句我们加了一个 1=1 的绝对true的语句,目的是为了防止语句错误,变成 SELECT * FROM student WHERE 这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用 元 素。
<select id="findUserById" resultType="com.xinzhi.entity.User">
select id,username,password from user
<where>
<if test="id != null and id != ''">
AND id = #{id}
</if>
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="password != null and password != ''">
A ND password = #{password}
</if>
</where>
</select>
5、trim元素
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着 我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉 的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND">
<if test="username != null and username != ''">
AND username LIKE concat('%', #{username}, '%')
</if>
<if test="id != null">
AND id = #{id}
</if>
</trim>
</select>
6、set元素
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注 意:set元素遇到,会自动把,去掉。
<update id="update">
UPDATE user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="password != null and password != ''">
password = #{password}
</if>
</set>
WHERE id = #{id}
</update>
7、foreach元素
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
- collection配置的是传递进来的参数名称
- item配置的是循环中当前的元素。
- index配置的是当前元素在集合的位置下标。
- open和 close配置的是以什么符号将这些集合元素包装起来。
- open和 close配置的是以什么符号将这些集合元素包装起来。
8、SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽 取出来,然后使用时直接调用。
提取SQL片段:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace
-->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
六、配置文件解读
mybatis的配置文件分为核心配置文件和mapper配置文件
1、核心配置文件
-
mybatis-config.xml 系统核心配置文件
-
核心配置文件主要配置mybatis一些基础组件和加载资源,核心配置文件中的元素常常能影响 mybatis的整个运行过程。
-
能配置的内容如下,顺序不能乱:
1.properties是一个配置属性的元素 2.settings设置,mybatis最为复杂的配置也是最重要的,会改变mybatis运行时候的行为 3.typeAliases别名(在TypeAliasRegistry中可以看到mybatis提供了许多的系统别名) 4.typeHandlers 类型处理器(比如在预处理语句中设置一个参数或者从结果集中获取一个参数时 候,都会用到类型处理器,在TypeHandlerRegistry中定义了很多的类型处理器) 5.objectFactory 对象工厂(myabtis在构建一个结果返回的时候,会使用一个ObjectFactory去 构建pojo) 6.plugins 插件 7.environments 环境变量 environment 环境变量 transactionManager 事务管理器 dataSource 数据源 databaseIdProvider 数据库厂商标识 8.mappers 映射器
(1)environments
environments可以为mybatis配置多环境运行,将SQL映射到多个不同的数据库上,必须指定其中一个 为默认运行环境(通过default指定),如果想切换环境修改default的值即可
<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>
<environment id="product">
<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>
-
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
数据源是必须配置的。
-
有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]") - unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。 - pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。 - jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中 或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
-
数据源也有很多第三方的实现,比如druid,hikari,dbcp,c3p0等等…
-
这两种事务管理器类型都不需要设置任何属性。
-
具体的一套环境,通过设置id进行区别,id保证唯一!
-
子元素节点:transactionManager - [ 事务管理器 ]
<!-- 语法 --> <transactionManager type="[ JDBC | MANAGED ]"/>
-
子元素节点:数据源(dataSource)
(2)mappers
**mappers的存在就是要对写好的mapper和xml进行统一管理 **
要不然系统怎么知道我写了哪些mapper
引入:
<mappers>
<!-- 使用相对于类路径的资源引用 -->
<mapper resource="com/xinzhi/dao/userMapper.xml"/>
<!-- 面向注解时使用全类名 -->
<mapper class="com.xinzhi.dao.AdminMapper"/>
</mappers>
Mapper文件
<?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.xinzhi.mapper.UserMapper">
</mapper>
- namespace中文意思:命名空间,作用如下:
- namespace的命名必须跟某个接口同名,这才能找的到啊!
(3)Properties
数据库连接信息我们最好放在一个单独的文件中。
-
在资源目录下新建一个db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false username=root password=root
-
将文件导入properties 配置文件
<configuration> <properties resource="db.properties" /> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="com.assin.datasource.HikariDataSource"> <property name="driverClassName" value="${driver}"/> <property name="jdbcUrl" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/userMapper.xml"/> </mappers> </configuration>
(5)其他配置浏览
settings能对我的一些核心功能进行配置,如懒加载、日志实现、缓存开启关闭等
<settings>
<!---->
<setting name="cacheEnabled" value="true"/>
<!---->
<setting name="lazyLoadingEnabled" value="true"/>
<!---->
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods"
value="equals,clone,hashCode,toString"/>
</settings>
七、日志配置
1、标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
STD:standard out:输出
STDOUT_LOGGING:标准输出日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2、组合log4j完成日志功能(扩展)
-
导入log4j 包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>${slf4j.version}</version> </dependency>
-
配置文件编写 log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代 码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/xinzhi.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
setting设置日志实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
八、数据关系处理
- 部门和员工的关系,一个部门多个员工,一个员工属于一个部门
- 那我们可以采取两种方式来维护关系,一种在一的一方,一种在多的一方!
1、手动维护
自己维护,多写几个语句,使用java组装,有时候使用mybatis维护关系反而复杂,不如自己维 护。
- 先搜索部门
- 根据部门搜索员工
- 手动拼装
2、在多的一方维护关系
多对一:
- 多个员工,对应一个部门
- 对于员工而言,关联–多个员工,关联一个部门【多对一】
- 对于部门而言,集合–一个部门,有很多个员工【一对多】
-
实体类
@Data public class Dept implements Serializable{ private int id; private String name; } @Data public class Employee implements Serializable { private int id; private String name; //维护关系 private Dept dept; }
-
实体类对应的Mapper接口
public interface DeptMapper { } public interface EmployeeMapper { }
-
Mapper接口对应的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="com.xinzhi.dao.DeptMapper"> </mapper> <?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.xinzhi.dao.EmployeeMapper"> </mapper>
(1)按查询嵌套 , 级联查询处理,类似与SQL中的子查询
- 方法
List<Employee> findAllEmployees();
-
mapper处理
<?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.assin.dao.EmployeeMapper"> <resultMap id="EmployeeDept" type="com.assin.entity.Employee"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的 表中的列名--> <association property="dept" column="dId" javaType="com.assin.entity.Dept" select="getDept"/> </resultMap> <select id="findAllEmployees" resultMap="EmployeeDept"> select * from employee </select> <select id="getDept" resultType="com.assin.entity.Dept"> select * from dept where id = #{id} </select> </mapper>
-
在mybatis配置文件中 进行注册Mapper
-
测试
@Test public void testFindAllEmployees() { EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); List<Employee> allEmployees = mapper.findAllEmployees(); for (Employee allEmployee : allEmployees) { System.out.println(allEmployee); } }
(2)按结果嵌套处理 类似SQL中的联表查询
-
接口方法
List<Employee> findAllEmployees2();
-
对应的mapper文件
<select id="findAllEmployees2" resultMap="EmployeeDept2" > select e.id eid,e.name ename, d.id did,d.name dname from employee e left join dept d on d.id = e.did </select> <resultMap id="EmployeeDept2" type="com.xinzhi.entity.Employee"> <id property="id" column="eid"/> <result property="name" column="ename"/> <!--关联对象property 关联对象在Student实体类中的属性--> <association property="dept" javaType="com.xinzhi.entity.Dept"> <id property="id" column="did"/> <result property="name" column="dname"/> </association> </resultMap>
-
mybatis文件配置注册
<mapper resource="com/assin/dao/DeptMapper.xml"/> <mapper resource="com/assin/dao/EmployeeMapper.xml"/>
-
测试
@Test public void testFindAllEmployees2() { EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); List<Employee> allEmployees = mapper.findAllEmployees2(); for (Employee allEmployee : allEmployees) { System.out.println(allEmployee); } }
3、在一的一方维护关系
-
实体类
@Data public class Dept implements Serializable{ private int id; private String name; //用户关系维护 List<Employee> employees; } @Data public class Employee implements Serializable { private int id; private String name; }
(1)按查询嵌套 , 级联查询处理,类似与SQL中的子查询
-
接口方法
public interface DeptMapper { Dept findDeptById(int id); }
-
写配置文件
<select id="findDeptById" resultMap="DeptEmployee"> select e.id eid,e.name ename, d.id did,d.name dname from dept d left join employee e on d.id = e.did where e.id=#{id} </select> <resultMap id="DeptEmployee" type="com.xinzhi.entity.Dept"> <id property="id" column="did"/> <result property="name" column="dname"/> <collection property="employees" ofType="com.xinzhi.entity.Employee"> <result property="id" column="eid" /> <result property="name" column="ename" /> </collection> </resultMap>
-
Mapper文件注册
<mappers> <mapper resource="com/xinzhi/dao/DeptMapper.xml"/> </mappers>
(2)按结果嵌套处理 类似SQL中的联表查询
1、接口编写方法
Dept findDeptById2(int id);
2、接口编写方法
<resultMap id="DeptEmployee2" type="com.xinzhi.entity.Dept">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="employees" javaType="ArrayList"
ofType="com.xinzhi.entity.Employee" column="id" select="getEmployeeById"/>
</resultMap>
<select id="findDeptById2" resultMap="DeptEmployee2">
select * from dept where id = #{id}
</select>
<select id="getEmployeeById" resultType="com.xinzhi.entity.Employee">
select * from employee where did = #{id}
</select>
3、mapper注册
<mappers>
<mapper resource="com/xinzhi/dao/DeptMapper.xml"/>
</mappers>
九、MyBatis缓存
1、缓存的作用?
- 如果缓存中有数据,就不用从数据库获取,直接从内存获取,大大提高系统性能。
- mybatis提供一级缓存和二级缓存
2、一级缓存:
一级缓存是sqlsession级别的缓存
- 在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存 数据
- 不同的sqlsession之间的缓存区域是互相不影响的。
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
一级缓存工作原理
测试步骤:
- 开启日志!
- 测试在一个Session中查询两次相同记录
- 查看日志输出
缓存失效的情况:
- 查询不同的东西;
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的Mapper.xml
- 手动清理缓存!
来源与:(63条消息) 【狂神说】Mybatis学习笔记(全)_li643937579的博客-CSDN博客_狂神说mybatis笔记
一级缓存失效
- sqlSession不同
- 当sqlSession对象相同的时候,查询的条件不同,
- 原因是第一次查询时候一级缓存中没有第二次 查询所需要的数据 3. 当sqlSession对象相同,两次查询之间进行了插入的操作
- 当sqlSession对象相同,手动清除了一级缓存中的数据
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!
一级缓存相当于一个Map
3、二级缓存
二级缓存是mapper级别的缓存
- 多个sqlsession去操作同一个mapper的sql语句,多个sqlsession可以共用二级缓存,所得到的数 据会存在二级缓存区域,
- 二级缓存是跨sqlsession的
- 二级缓存相比一级缓存的范围更大(按namespace来划分),多个sqlsession可以共享一个二级缓 存
首先要手动开启mybatis二级缓存。 在config.xml设置二级缓存开关 , 还要在具体的mapper.xml开启二级缓存
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 需要将映射的javabean类实现序列化 -->
<!--开启本Mapper的namespace下的二级缓存-->
<cache eviction="LRU" flushInterval="100000"/>
(1)cache属性
- eviction回收策略(缓存满了的淘汰机制),目前MyBatis提供以下策略。
- LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
- LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
- LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
- WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU, 移除最长时间不用的对形象
- flushInterval:刷新间隔时间,单位为毫秒,
- flushInterval:刷新间隔时间,单位为毫秒,
- size:引用数目
- 一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。 这里配置的是1024个对象
- readOnly:只读,
- 意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有办法修改缓存,他的默认值是false,不允许我们修改
(2)操作过程
sqlsession1查询用户id为1的信息,查询到之后,会将查询数据存储到二级缓存中。
如果sqlsession3去执行相同mapper下sql,执行commit提交,会清空该mapper下的二级缓存区域的 数据
sqlsession2查询用户id为1的信息, 去缓存找 是否存在缓存,如果存在直接从缓存中取数据
-
禁用二级缓存:
在statement中可以设置useCache=false,禁用当前select语句的二级缓存,默认情况为true
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" useCache="false">
在实际开发中,针对每次查询都需要最新的数据sql,要设置为useCache=“false” ,禁用二级缓存
-
flushCache标签:刷新缓存(清空缓存)
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" flushCache="true">
一般下执行完commit操作都需要刷新缓存,flushCache="true 表示刷新缓存,可以避免脏读
-
二级缓存应用场景
对于访问多的查询请求并且用户对查询结果实时性要求不高的情况下,可采用mybatis二级缓存,降低 数据库访问量,提高访问速度,如电话账单查询 根据需求设置相应的flushInterval:刷新间隔时间,比如三十分钟,24小时等。
- 二级缓存局限性:
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品 信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就 无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓 存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类 问题需要在业务层根据需求对数据有针对性缓存
4、第三方缓存
三方缓存组件很多,最常用的比如ehcache,Memcached、redis等