目录
一 、MyBatis简单介绍
1、历史简介
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
2、特点
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的pojo映射成数据库中的记录
- MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
- 简单易学,没有任何第三方依赖。
- 灵活,MyBatis 不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
二、使用mybatis
1、导入依赖
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
2、编写核心配置文件 mybatis-config.xml
注:核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
<?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"/>
<!-- 设置别名 -->
<typeAliases>
<!-- 方法一 给单个类起别名 -->
<!-- type:对应的类路径 alias:别名-->
<typeAlias type="" alias=""/>
<!-- 方法二 给某个包里面的所有类起别名,别名为类名 -->
<!-- name:包路径-->
<package name=""/>
</typeAliases>
<!--配置数据库信息-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="1010"/>
</dataSource>
</environment>
</environments>
<!-- 引入映射文件 -->
<mappers>
<!-- 引入单个映射文件 -->
<mapper resource="com/mapper/XXMapper.xml"/>
<!-- 引入某个包里的所有映射文件 -->
<package name="com.mapper"/>
</mappers>
</configuration>
3、创建mapper接口 XXMapper
public interface XXMapper {
List<XX> select();
int insert(XX xx);
int update(XX xx);
int delete(XX xx);
}
4、编写接口映射文件 XXMapper
<?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.mapper.XXMapper">
<!-- 注:id为对应方法名,参数类型可使用 @Param 指定 或 Map 类型传入-->
<!-- 查询 -->
<select id="" parameterType="" resultType="">
<!--查询语句-->
</select>
<!-- 插入 -->
<insert id="" parameterType="">
<!--插入语句-->
</insert>
<!-- 修改 -->
<update id="" parameterType="">
<!--修改语句-->
</update>
<!-- 删除 -->
<delete id="" parameterType="">
<!--删除语句-->
</delete>
</mapper>
<!-- 注:多个参数可使用 @Param 指定 或 Map 类型传入-->
5、编写测试类
public class MyTest {
@Test
public void test() throws IOException {
//加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-conf.xml");
//通过 SqlSessionFactoryBuilder 获取 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession
SqlSession session = sqlSessionFactory.openSession();
//获取Mapper接口对象
XXMapper mapper = session.getMapper(XXMapper.class);
//调用mapper接口中的方法执行操作
//增删改方法执行后需要调用session.commit()方法提交事务
session.close();
}
}
6、配置log4j日日志功能(非必须)
a、导入依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
b、添加配置文件 log4j.xml,存放在src/main/resources目录下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<!-- (上面爆红的不管,不影响) -->
<!-- level:设置日志级别 -->
<!-- FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)-->
<!-- 从左到右打印的内容越来越详细-->
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
三、 MyBatis获取参数值
1、获取单个参数值
直接通过 #{参数名} 获取即可,参数名可以任意
User selectUser(String username);
<select id="selectUser" resultType="User">
select * from user where username = #{username}
</select>
2、获取多个参数值
当接口方法中有多个参数时,MyBatis会将多个参数保存在Map集合中,会以两种方式存储:
Map集合的键为arg0,arg1...后面的数字表示对应的参数位置(从0开始)
Map集合的键为param1,param2..后面的数字表示对应参数位置(从1开始)
两种方式是同时保存的,可用任意一种获取。
a、通过#{arg0}或#{param1}获取对应位置上的参数
User login(String username, String password);
<select id="login" resultType="User">
select * from user where username = #{arg0} and password = #{arg1}
</select>
<!--或者 select * from user where username = #{param1} and password = #{param2} -->
<!--也可以混合使用 select * from user where username = #{arg0} and password = #{param2} -->
b、手动将参数保存在Map集合中,并传入Map集合作为参数,通过#{自定义键名}获取
和上面的一样,只不过Map是自定义的,并且只需要传入Map集合作为参数
User login(Map<String,String>);
c、使用 注解@Param("")在接口方法中指定参数
该方式的原理和上两个一样,MyBatis会解析并将值保存在Map集合中,但是键是自定义的即@Param("")中的值
User login(@Param("username") String username,@Param("password") String password);
之后通过#{username},#{password}获取。
和第一种的区别就是,将MyBatis帮我们设置的键名arg0改成自定义username
除此之外,也可以继续通过#{param1}获取
d、传入实体类作为参数
该形式也是直接通过 #{属性名} 即可获得对应的值
User login(User user);
四、MyBatis设置自定义结果集 resultMap
1、字段名和实体类中的属性名不一致
a、可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
b、字段名符合数据库规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)
在MyBatis的核心配置文件中设置全局配置信息mapUnderscoreToCamelCase为true,
之后在查询表中数据时,MyBatis会自动将_类型的字段名转换为驼峰
<setting name="mapUnderscoreToCamelCase" value="true"/>
如 user_name ----> userName
c、使用resultMap处理字段和属性的映射关系
<!--设置字段名和属性名之间的映射-->
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="userName" column="user_name"></result>
<result property="password" column="password"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</resultMap>
<!--List<User> selectUser();-->
<select id="selectUser" resultMap="userMap">
select * from t_user
</select>
2、多对一映射处理
以员工和部门为例,多个员工可以属于一个部门(即多对一)
a、使用级联方式处理映射关系
<resultMap id="empAndDept" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="did" property="dept.did"></result>
<result column="dname" property="dept.dname"></result>
</resultMap>
b、使用association处理映射关系
<resultMap id="empAndDept" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<!-- 使用 association 关联部门信息 -->
<association property="dept" javaType="Dept">
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
</association>
</resultMap>
c、使用分布查询
<resultMap id="empAndDept" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<!--
使用分布查询关联部门信息,select中的值代表部门映射文件中通过id查询部门的方法
property:属性名
select:对应分布查询的查询语句,输入值为包.方法名
column:select查询方法中的查询条件
-->
<association property="dept"
select="com.mapper.DeptMapper.getDeptById" column="did">
</association>
</resultMap>
对应的部门映射文件:
<!--
对应Mapper接口中的方法:
Dept getDeptById(@Param("did") int did);
-->
<select id="getDeptById" resultType="Dept">
select * from t_dept where did = #{did}
</select>
3、一对多映射处理
以部门和员工为例,一个部门可以有多个员工(即一对多)
a、使用 collection 进行映射
<resultMap id="deptAndEmp" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<!--
ofType:对应集合中的对象类型
-->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="ename" column="ename"></result>
</collection>
</resultMap>
b、使用分布查询
<resultMap id="deptAndEmp" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<collection property="emps"
select="com.mapper.EmpMapper.getEmpListByDeptId" column="did">
</collection>
</resultMap>
对应的员工映射文件:
<!--
对应接口中的方法:
List<Emp> getEmpListByDeptId(@Param("did") int did);
-->
<select id="getEmpListByDeptId" resultType="Emp">
select * from t_emp where did = #{did}
</select>
五、Mybatis的延迟加载
延迟加载,顾名思义就是先不加载,等后面需要的时候再加载,大概意思就是你调用了相关的查询方法,但是你没有用到查询结果,那么该查询语句实际上是没有执行的。就是把要执行的语句等到你需要真正用到的时候再执行。
比如员工和部门表中,当你通过多对一中的分布查询来查员工信息时(此时查询员工和查询部门的方法是绑定在一起的),如果你在后面只是需要用到员工的id或名称那么mybatis只会帮我们查询员工信息表,不会查询部门表。如果你只是要获取部门信息,那就只执行查询部门的语句。
注:延迟加载在一对多和多对一的分布查询中使用
<!-- 在mybatis核心配置文件中开启全局延迟加载 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
一旦开启,所有关联对象都会延迟加载。可通过在association 或 collection标签中的fetchType属性来设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"
六、特殊sql语句的执行
1、模糊查询
<!--List<User> selectByLike(@Param("value") String value);-->
<select id="selectByLike" resultType="User">
<!-- 方式一 -->
select * from t_user where username like '%${value}%'
<!-- 方式二 -->
select * from t_user where username like concat('%',#{value},'%')
<!-- 方式三 -->
select * from t_user where username like "%"#{value}"%"
</select>
2、批量删除
<!--int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
3、动态设置表名
<!--List<User> getAllUser(@Param("tableName") String tableName);-->
<select id="getAllUser" resultType="User">
select * from ${tableName}
</select>
4、在插入数据后获取自增的主键
<!-- useGeneratedKeys:设置开启自增 keyProperty:插入完成后返回的属性值 -->
<!--int insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(null,#{username},#{password},#{age},#{sex})
</insert>
上面插入代码执行后,会把自增的 id 值赋给之前 null 的 id ,之后直接获取当前id值就行了
七、动态sql
根据标签(如if、where、trim、choose、when、otherwise、foreach等)动态拼接sql语句
1、if
if 标签可通过对test属性的表达式进行判断,若表达式的结果为true,则执行标签中的内容,否则不执行。
<!-- 在test中输入要判断的表达式 --> <if test=""> <!-- 要拼接的sql语句 --> </if>
2、where
where 标签帮我们在语句中添加where关键字,一般和 if 标签 搭配使用:
如果 if 标签中的条件满足则拼接并可以帮我们去掉前面多余的and或or(无法去掉拼接语句后面的);
如果 where 标签里面的 if 标签都不满足条件,则不会添加where关键字;
<where> <if test=""> <!-- 要拼接的sql语句 --> </if> <if test=""> <!-- 要拼接的sql语句 --> </if> </where>
3、trim
trim 标签可以动态的 去掉 或 添加 标签中的内容
trim 中的属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
<trim prefix="XX" prefixOverrides="XX" suffix="XX" suffixOverrides="XX" > </trim>
4、choose、when、otherwise
这三个标签搭配使用可以实现if(){}else if(){}else的
<choose>
<when test="">
<!-- 要拼接的sql语句 -->
</when>
<when test="">
<!-- 要拼接的sql语句 -->
</when>
<when test="">
<!-- 要拼接的sql语句 -->
</when>
<otherwise>
<!-- 要拼接的sql语句 -->
</otherwise>
</choose>
5、foreach
foreach 标签中的属性:
collection:要循环的数组或集合
item:集合或数组中的每一个数据
separator:每个循环体之间的分隔符
open:在foreach标签内容的开始处添加的内容
close:在foreach标签内容的结尾处添加的内容
例:根据id删除多条用户信息
<delete id="deleteBYIds">
delete from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
八、MyBatis的缓存
MyBatis中的缓存有两个:一级缓存和二级缓存,默认为以及缓存。‘
缓存只有在查询时才存在(才有有意义),增删改没有缓存概念。
1、一级缓存
一级缓存是SqlSession级别的,即在同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。(即同一个查询语句只执行一次)。一级缓存默认是开启的,我们不需要配置任何东西。
注意,以下操作会清除一级缓存:
在两次查询期间执行了增删改操作;
提交或关闭SqlSession;
收到清除了缓存;
2、二级缓存
二级缓存是SqlSessionFactory级别即在同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存。
二级缓存需要手动开启:
1、在核心配置文件中开启,这个看版本,有些版本默认开启,有些默认关闭。
<settings> <setting name = "cacheEnabled" value = "true" /> </settings>
2、在xxMapper.xml映射文件中使用 <cache /> 标签开启
<!-- cache中的属性: eviction:缓存回收策略 LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。(默认) FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。 SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 flushInterval:设置刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。 size:代表缓存最多可以存储多少个对象,正整数。 readOnly:只读,true/false true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。,能会好一些。 false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全(默认)。 --> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
注意:二级缓存必须在SqlSession关闭或提交之后有效,并且对应的查询出来的实体类需要实现序列化。
缓存查询的顺序:二级 - -> 一级 - -> 数据库
先查询二级缓存是因为可能在其他 SqlSession 中查过有缓存记录了。
如果在二级和一级缓存中都没有则查询数据库。在数据库中查询的数据会先放在一级缓存中,当 SqlSession 关闭后,会把一级缓存会写入二级缓存。
九、MyBatis的逆向工程
在创建好数据库表,使用MyBatis的逆向工程负责根据数据库表,反向生成 Java实体类、 Mapper接口 和 Mapper映射文件。
创建步骤/模板:
1、添加所需插件
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
2、创建逆向工程的配置文件:generatorConfig.xml(名字是固定的)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 上面爆红的可以不管,不影响-->
<!--
targetRuntime: 执行生成的逆向工程的版本
-->
<context id="contextId" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="root">
</jdbcConnection>
<!-- javaBean的生成策略
targetPackage:生成的实体类所在的包
targetProject:生成的实体类所在的位置(.表示工程路径)
-->
<javaModelGenerator targetPackage="com.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.mapper"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--
设置需要逆向生成的表,可以通过多个 table 标签设置多个表
tableName:表名
domainObjectName:实体类的类名
-->
<table tableName="t_user" domainObjectName="User"/>
</context>
</generatorConfiguration>
3、在maven管理中双击执行generate
此时会在相应目录生成相应文件,其中在XXExample类中提供了多种增删改查方法。
十、 Mybatis分页插件的使用
1、添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
2、在核心配置文件中配置分页插件
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3、在查询功能前使用PageHelper.startPage((int pageNum, int pageSize)开启分页功能
其中,pageNum表示当前页的页码,pageSize表示每页显示的数量
4、查看分页信息
//list:分页之后的数据集合
PageInfo<T> pageInfo = new PageInfo<>(List<T> list)
在上面代码中传入查询到的结果集合即可获取分页信息,包括:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
更多内容可以查看 官网