MYbatis讲解
什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
随谈
目前我只是简单的讲解mybatis的使用,如果想学到更多可以去:https://mybatis.net.cn/
这是mybatis中文文档,所以的mybatis操作都可以在上面找到,还有很多源码之类的分析
入门
打开idea,创建一个maven项目
导入依赖的jar包
<!-- 导入依赖 -->
<dependencies>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis的jar包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lombok可以注解来注入实体类的get,set等方法,在实际开发中最好不用,会报一下莫名其妙的错,如果想学习知识什么的就很好用 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- log4j日志jar包 -->
<!-- <dependency>-->
<!-- <groupId>log4j</groupId>-->
<!-- <artifactId>log4j</artifactId>-->
<!-- <version>1.2.17</version>-->
<!-- </dependency>-->
<!--分页要用的jar包-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
maven资源导出问题(约定大于配置)
<!-- 在build中配置resources,来防止我们资源导出失败的问题 -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
maven解决utf-8乱码问题
<!-- 解决xml中乱码问题 -->
<!-- 也可以把xml中的 utf-8 改为 utf8 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
全局配置
必须按照如下顺序来配置子标签
<configuration> //(配置)
<!-- 配置顺序如下
properties (属性)
settings (设置)
typeAliases (类别别名)
typeHandlers (类别处理器)
objectFactory (对象工厂)
plugins (插件)
environments (环境配置)
environment (环境变量)
transactionManager (事务管理器)
dataSource (数据源)
databaseldProvider (数据库厂商标识)
mappers (映射器)
-->
</configuration>
子标签 我讲的不全 可以去看 https://mybatis.net.cn/configuration.html#databaseIdProvider
< properties > 属性
用来调用properties文件,用${}占位符来快速引用数据源的消息
<!-- 引入外部配置文件 -->
<properties resource="db.properties">
<!-- 可以在内部添加值 -->
<property name="username" value="root"/>
</properties>
< settings > 设置
开启延迟加载(懒加载): 解决n+1问题
<setting name="lazyLoadingEnabled" value="true"/>
开启二级缓存(全局缓存)
<settings name="cacheEnabled" value="true"/>
开启日志
<setting name="logImpl" value="STDOUT_LOGGING"/> //自带的日志
<setting name="logImpl" value="LOG4J"/> //log4j日志 需要导jar包和配置log4j的资源 log4j.properties 文件
< typeAliases > 类别别名
取别名(一般来说给实体类取别名就行了)
//第一种方式 自定义
<typeAliases>
<typeAlias type="com.zhao.pojo.Student" alias="student"/>
</typeAliases>
//第二种方式 mybatis自己配置 可以给自己的配置类取别名 别名就是 实体类名字 第一个字母为大写其他小写
//如:第一个方法的 Student 实体类 在com.zhao.pojo包下 别名就是Student
<typeAliases>
<package name="com.zhao.pojo"/>
</typeAliases>
< typeHandlers > 类别处理器
用于处理 Java 类型和 Jdbc 类型之间的转换,用的不多可以不管
< objectFactory > 对象工厂
用于创建对象实例,用的不多可以不管
< plugins > 插件
可以用来配置mybatis的插件,比如pageHelper分页插件:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
< environments > 环境配置
**< transactionManager > ** 事务管理器
在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”):
-
JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。 它依赖于从数据源得 到的连接来管理事务范围。
-
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让 容器来管理事务的整个生命周期(比如 Spring 或 JEE 应用服务器的上下文) 默认 情况下它会关闭连接。 然而一些容器并不希望这样, 因此如果你需要从连接中停止 它,将 closeConnection 属性设置为 false。
< dataSource > 数据源
-
POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
-
UNPOOLED:采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
-
JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样
<!--注册JDBC-->
<environments default="development">
<environment id="development"> //环境变量
<transactionManager type="JDBC"/> //事务管理器
<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>
< databaseldProvider > 数据库厂商标识
MyBatis 可以根据不同的数据库厂商执行不同的语句,用的不多可以不管
< mappers > 映射器
用来配置 mapper.xml 映射文件
mapper class : 可以自动映射同一个类下同一个名字的mapper.xml文件 :比如我的 dao 类下有个 叫 UserMapper 的接口文件和 UserMapper.xml文件,就可以映射到这个文件
mapper resource : 就是很笨的导入 UserMapper.xml 文件
package name :就是自动扫描 我写的 dao 下面的所有 Mapper.xml 文件
<!--绑定接口-->
<mappers>
<mapper class="com.zhao.dao.UserMapper"/>
<mapper class="com.zhao.dao.ProviderMapper"/>
<!-- <mapper resource="com.zhao.dao.UserMapper.xml" />-->
<!-- <package name="com.zhao.dao"/>-->
</mappers>
注解开发
我感觉CRUD没啥必要用注解,现阶段可以多手敲,而且如果要后期调配啥的注解很难得找(就是很难维护)没有直接写在Mapper.xml文件里面好找
值得一提的是 @Param 注解,若需要传入多个参数,可以结合 @Param 注解
如:@Param(“uid”) int id 在mapper.xml文件中写sql时 只需要用 #{uid} 把传递参数时就可以把 id 值传给sql语句
@Select("select * from user")
List<User> getUsers();
/*
@Param 注解 可以引用多个值
编写对应的sql语句时要对应 @Param 注解中的值
只能用于基本类型 像 POJO(javaBean) 中的不能引用
* */
@Delete("deelete from user where id = #{uid} and name = #{uname}")
int dedleteUser(@Param("uid") int id,@Param("uname") String name);
主键返回
1.使用 useGeneratedKeys 和 keyProperty 属性
<insert id="test2" parameterType="Address" useGeneratedKeys="true" keyProperty="id">
insert into smbms_address (contact,addressDesc,tel) values (#{contactName},#{desc},#{phone})
</insert>
2.使用**< selectKey >**子标签
<insert id="test2" parameterType="Address">
insert into smbms_address (contact,addressDesc,tel) values (#{contactName},#{desc},#{phone})
<selectKey keyProperty="id" order="AFTER" resultType="int" > <!--after:在执行sql语句后执行-->
SELECT LAST_INSERT_ID();
</selectKey>
</insert>
此时可以用**< selectKey >标签,设置其order属性为BEFORE**,并在标签体内写上生成主键的SQL语句,这样在插入之前,会先处理**< selectKey >**,生成主键,再执行真正的插入操作。
**< selectKey >**标签其实就是一条SQL,这条SQL的执行,可以放在主SQL执行之前或之后,并且会将其执行得到的结果封装到入参的Java对象的指定属性上。注意 **< selectKey >子标签只能用在< insert >和< update >**标签中。上面的 LAST_INSERT_ID() 实际上是MySQL提供的一个函数,可以用来获取最近插入或更新的记录的主键id。
关联查询(一对多,多对一)查询
多对一:对象:association
解决问题(实体类中:一个实体类对应另一个实体类):private Teacher teacher;
<?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.zhao.dao.StudentMapper">
<!--
思路:
1.查询所有的学生信息
2.根据查询出来的学生的tid,寻找对应的老师
(类似 子查询)
-->
<select id="getStudent" resultMap="StudentTeacher">
-- select s.id,s.name,t.name from student s,teacher t where s.tid = t.id;
select * from student
</select>
<resultMap id="StudentTeacher" type="com.zhao.pojo.Student">
<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
<!-- property:主键在pojo中的属性名 -->
<!-- column:主键在数据库中的列名 -->
<result property="id" column="id" />
<result property="name" column="name" />
<!--
复杂的属性,我们需要单独处理
对象( pojo ( javaBean )): association
集合: collection
-->
<association property="teacher" column="tid" javaType="com.zhao.pojo.Teacher" select="getTeacher" />
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
<!--===========================================================================================================-->
<!--按照结果嵌套处理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id teacherId
from student s,teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="com.zhao.pojo.Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<association property="teacher" javaType="com.zhao.pojo.Teacher" >
<result property="id" column="teacherId" />
<result property="name" column="tname" />
</association>
</resultMap>
</mapper>
一对多:集合:collection
解决问题(实体类中:一个实体类对应一个List):private List< Student > students;
<?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.zhao.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid = t.id and t.id = #{id}
</select>
<resultMap id="TeacherStudent" type="com.zhao.pojo.Teacher">
<result property="id" column="tid" />
<result property="name" column="tname" />
<!--
复杂的属性,我们需要单独处理 对象:association 集合:collection
javaType=“” 指定属性的类型
集合中的泛型信息,我们使用ofType获取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
</mapper>
动态SQL
比如在以前的开发中,由于不确定查询参数是否存在,许多人会使用类似于where 1 = 1 来作为前缀,然后后面用AND 拼接要查询的参数,这样,就算要查询的参数为空,也能够正确执行查询,如果不加1 = 1,则如果查询参数为空,SQL语句就会变成SELECT * FROM student where,SQL不合法。
if
<!-- 示例 -->
<select id="find" resultType="student" parameterType="student">
SELECT * FROM student WHERE age >= 18
<if test="name != null and name != ''">
AND name like '%${name}%'
</if>
</select>
choose
<!-- choose 和 when , otherwise 是配套标签
类似于java中的switch,只会选中满足条件的一个
-->
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<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>
</select>
trim:基本使用where和set
where
标签只会在至少有一个子元素返回了SQL语句时,才会向SQL语句中添加WHERE,并且如果WHERE之后是以AND或OR开头,会自动将其删掉
<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>
set
在至少有一个子元素返回了SQL语句时,才会向SQL语句中添加SET,并且如果SET之后是以,
开头的话,会自动将其删掉
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title !=null">
title=#{title},
</if>
<if test="author !=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
sql
可将重复的SQL片段提取出来,然后在需要的地方,使用**< include >**标签进行引用,实现了代码的复用
<select id="findUser" parameterType="user" resultType="user">
SELECT * FROM user
<include refid="whereClause"/>
</select>
<sql id="whereClause">
<where>
<if test user != null>
AND username like '%${user.name}%'
</if>
</where>
</sql>
bind
mybatis的动态SQL都是用OGNL表达式进行解析的,如果需要创建OGNL表达式以外的变量,可以用bind标签
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
foreach
用来做迭代拼接的,通常会与SQL语句中的IN查询条件结合使用,注意,到parameterType为List(链表)或者Array(数组),后面在引用时,参数名必须为list或者array。如在foreach标签中,collection属性则为需要迭代的集合,由于入参是个List,所以参数名必须为list
<select id="batchFind" resultType="student" parameterType="list">
SELECT * FROM student WHERE id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
缓存
一级缓存
默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数 据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库
-
一级缓存在下面情况会被清除
-
在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
-
SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
-
对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
-
在全局配置文件中设置 < setting name=“localCacheScope” value=“STATEMENT”/>,这样会使一级缓存失效,二级缓存不受影响
-
二级缓存
默认关闭,可通过全局配置文件中的< settings name=“cacheEnabled” value=“true”/>开启二级缓存总开关,然后在某个具体的mapper.xml中增加< cache />,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中
插件:PageHelper分页插件
注意:在编写mapper.xml的时候,SQL语句的结尾不要带上;
,因为PageHelper插件是在SQL末尾拼接LIMIT
关键字来进行分页的,若SQL语句带上了;
,就会造成SQL语法错误
在pom.xml配置maven依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
在mybatis全局配置文件里面添加插件的标签
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
测试类
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
PageHelper.startPage(2,3); //第二页,三条数据
/*要在打印出来之前写,不然就直接打印出来全部了*/
List<User> users = mapper.test1();
users.forEach(System.out::println);
sqlSession.close();
}
结果
我用了日志,出了红色框中的选项,其他的都是日志,日志也就是使用了动态代理,看java执行了哪些操作
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/74785.html