目录
1.简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
下面是mybatis的官方学习文档,我将用较为通俗的语言去说明mybatis的一些基本知识。
下方是我之前写的mybatis最基本的操作,只涉及最简单的增删改查。
五分钟实现mybatis增删改查。_过四级呀过四级的博客-CSDN博客
2.搭建环境
2.1依赖
下面是官方文档提供的依赖,只要在pom.xml中导入相应的版本即可。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2.SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。这是官方原话。创建SqlSessionFactory方法有两种,可以通过xml配置文件或纯java去实现,我们通常是通过xml文件进行配置。我们需要一个配置文件去获得SqlSessionFactoryBuilder,然后通过SqlSessionFactoryBuilder去得到SqlSessionFactory。
我们需要建一个mybatis-config.xml的配置文件去获取SqlSessionFactoryBuilder。
<?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核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.XML都需要在mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/LL/com.LL.dao/UserMapper.xml"/>
</mappers>
</configuration>
写一个工具类,mybatisUtils去获取SqlSessionFactoryBuilder。
public class mybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//使用Mybatis第一步:获取sqlSessionFactory对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream= Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
下面是官方文档给的源代码
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
通过工具类getSqlSession方法去获取SqlSession,SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
3.第一个mybatis程序
3.1导入依赖
<!-- 导入依赖-->
<dependencies>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
3.2创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
3.3编写接口类
public interface UserMapper{
//查询所有用户
List<User> getUserList();
}
3.4编写配置文件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">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.LL.dao.UserMapper">
<!-- 查询语句-->
<select id="getUserList" resultType="com.LL.pojo.User">
select * from mybatis.user
</select>
</mapper>
注意:
1.UserMapper.xml与UserMapper同名,放在同一包下。
2.namespace写的是UserMapper路径,select中的id代表绑定UserMapper中的类名,resultType则是返回值的类型
3.想要xml文件起作用,我们还需要在mybatis-config.xml中去注册该Mapper.xml
4.在这里我们默认实体类中的属性与数据库中字段命名完全一致,否则会取不到数值,如果不同时,我们需要用到resultMap,下面我们会讲到。
<mappers>
<mapper resource="com/LL/dao/UserMapper.xml"/>
</mappers>
我们通常使用resource和class,下面将class方式也写出来。
<mappers>
<mapper class="com.LL.dao.UserMapper"/>
</mappers>
3.5测试
首先我们需要通过工具类去获取Sqlsession,将UserMapper类型传入到sqlsession.getMapper中,创建一个UserMapper对象,直接调用接口中的方法即可实现对数据库的操作,最后不要忘记sqlsession.close,我们需要将获取的sqlsession归还到连接池中,否则会造成资源浪费。
@Test
public void test(){
SqlSession sqlSession = mybatisUtils.getSqlSession();
UserMapper mapper =sqlSession.getMapper(UserMapper.class);
List<User> userList =mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
以上就是一个最简单的mybatis程序。
4.关于mybatis-config.xml
4.1properties属性配置
下面是官方文档给的源代码,设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
我们可以建立一个db.properties文件来存储这些属性,只需要在mybatis-config.xml中加一句<properties resource="db.properties"/>即可。
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=root
4.2settings设置
我们可以在mybatis-config.xml中去去设置很多行为
比如我们比较常见的想要开启日志,就可以这样设置
<settings>
<!--标准日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
4.3typeAliases别名
在Mapper.xml中设置返回值类型是,我们需要设置包名resultType="com.LL.pojo.User",只要在mybatis-config.xml中加上下面的代码,那么我们我们可以只写resultType="user",文件会自动扫描该包下的类,达到降低冗余的效果。
我们还可以在类上加@Alias("user")注解,自定义类名。
<typeAliases>
<package name="com.LL.pojo"/>
</typeAliases>
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
5.XML映射文件
5.1select
在使用sql语句过程中,我们经常会使用带参数的sql语句,下面这条语句中的parameterType便是指#{id}的参数类型。
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
类似于下面这条语句
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
比较常用的还有resultMap,我们会在下面讲到
5.2insert, update 和 delete
这些方法与select大同小异,下面是一些示例
<insert id="addUser" parameterType="user" >
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id =#{id}
</delete>
5.3resultMap
resultMap这是比较重要的一个属性,需要单独拎出来讲。
如果我的实体类属性明明与数据库表中的字段名不同时,我们该如何去处理。
实体类
public class User {
private int id;
private String name;
private String password;
}
表中字段
此时在实体类中password与数据库中字段pwd不匹配,会导致取到的值为null,下面我们去解决这个问题。
<select id="getUserLimit" resultType="user" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
我们可以声明一个resultMap,id可以标识这个resultMap,type去声明类型。column表示字段名,property表示实体类属性名,这里的id,name数据库与表中命名相同可以省略。然后在select中resultMap="UserMap"是声明引用了哪一个resultMap。
5.4parameterType="map"
在上面的例子中,我们发现在sql语句中含有多个参数#{},我们如何去声明这些参数呢?
通过parameterType="map"我们可以发现,这条语句的参数类型是map,通过map去给这两个参数赋值,具体操作如下。
我们需要在该函数类型中传入一个map,只要在调用时传入一个map即可。注意#{}中的内容与map的key需要相同。
List<User> getUserLimit(Map<String,Integer> map);
map.put("startIndex",1);
map.put("pageSize",2);
List<User> list = userMapper.getUserLimit(map);
5.5高级结果映射
官方文档给的五表查询例子。
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
这是比较复杂的例子,不要慌,里面最常用两个属性association和collection,这两个属性分别代表着多对一和一对多。
例如:
在一个班级中,一名老师对应多名学生,多名学生对应同一名老师
5.5.1多对一
学生表
教师表
学生实体类
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
教师实体类
@Data
public class Teacher {
private int id;
private String name;
}
我们通过两种方式去查询所有的学生信息,下面是Mapper类和XML映射文件
public interface StudentMapper {
List<Student> getAllStudent();
List<Student> getAllStudent2();
}
<mapper namespace="com.LL.dao.StudentMapper">
<!--第一种方式-->
<select id="getAllStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
<!--第二种方式-->
<select id="getAllStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname ,t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
第一种:
我们的思路是先查询出所有的学生,然后根据教师id去查询相应的教师信息。重点是resultMap如何写。
type属性是声明这个类型是Student类型,id和name相同可以省略不写,重点是association属性,association中的property表示的是我们当前需要映射的属性是名称是teacher,column指的是数据库中的字段名,javaType指的是该属性的类型是Teacher类型,select指的是连接另一个sql语句进行操作。将tid的字段值作为一个参数,传到下面的getTeacher查询语句中。
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
第二种:
这是我们通常写的连表查询,resultMap要简单的多。
select s.id sid,s.name sname ,t.name tname
from student s,teacher t
where s.tid=t.id
association中的property指的还是当前属性的名字,JavaType指的还是当前属性的类型,我们通过result标签,为teacher中的name属性赋值为sql语句中查询的tname即可。
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
5.5.2一对多
学生类
@Data
public class Student {
private int id;
private String name;
private int tid;
}
教师类
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
我们通过两种方式去查询所有教师的信息及他的学生信息,下面是Mapper类和XML映射文件
public interface TeacherMapper {
//获取指定老师下的所有学生及老师信息
Teacher getTeacher(@Param("tid") int id);
Teacher getTeacher2(@Param("tid") int id);
}
<mapper namespace="com.LL.com.LL.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=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!--第二种子查询-->
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id =#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<result property="id" column="id"></result>
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherID">
</collection>
</resultMap>
<select id="getStudentByTeacherID" resultType="Student">
select * from student where tid=#{tid}
</select>
</mapper>
第一种
collection可以看做是集合查询,property是该属性的名字,ofType指的是集合的类型,通过result对students集合中的属性进行赋值。
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
第二种
collection中的property是该属性的名字,column是数据库中的字段,javaType指的是何种集合,ofType指的是集合的类型。将id属性值传给getStudentByTeacherID作为参数进行查询。
<resultMap id="TeacherStudent2" type="Teacher">
<result property="id" column="id"></result>
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherID">
</collection>
</resultMap>
6动态sql
动态 SQL 是 MyBatis 的强大特性之一。使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
这些用法与java中的用法类似
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
6.1if
这段代码就是如果title这个元素不为空,那么就会将if标签内个语句拼接到前面的sql语句中。
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
6.2choose、when、otherwise
choose就是进行选择,如果title不为空的话则拼接第一个when中的语句,如果author和author.name不为空则拼接第二个when中的语句,如果这两个条件都没有成立,则拼接otherwise中的语句。
<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>
6.3where
如果下面代码中state不为null,那么代码会按照正确的逻辑运行,但是如果state为空,其他两个代码不管哪一个为真,那么都会早整where后面直接加and,这就会造成代码错误。
<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>
</select>
我们只要将where变成标签,将代码放在where标签内,代码即可自动去掉前面多余的and。
<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>
6.4set
set可以去掉后面多余的逗号,防止sql语句拼接错误。
<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>
6.5foreach
这段sql的原句可以理解为 select * from mybatis.blog where (id=#{id1},id=#{id2},id=#{id3})
collection属性指的是传入的集合,item指的是遍历链表中的某一个值,open指的是在代码前加上某个字符串,close指的是在代码后加上某个字符串,separator指的是代码之间的分隔符,若凭空想象很难去写出这个代码,但是如果现将sql语句写出来,那么下面这段代码便会很容易写出来。
<select id="queryBlogFroeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
以上是我对mybatis的个人总结,如果存在问题还请指正。