1、本篇博客的目的和背景
目前我本人正在学习MyBatis框架,在原先了解并且懵懵懂懂使用的基础上,开始系统正式的学习。目前已经阐述了MVC架构模式和三层架构,明晰了在Web项目中的普遍编码层次,认识了框架,回顾了JDBC连接数据库,稍详细介绍了MyBatis框架。本篇博客我实践建立一个Maven项目。
2、我的上一篇博文
我的上一篇博文稍微详细的介绍了MyBatis框架,感兴趣的读者可以看一下,链接如下所示:
3、明确一下此项目创建的大概步骤
我这里直接先粘贴一张图片:
上面步骤中,第5步:创建Mapper的XML文件的时候,其实这个XML文件也属于配置文件,我们也可以放在resources目录下面,这样就可以被加载,不需要在POM文件的build标签中额外的配置。之所以需要在POM文件中额外的配置,即:我们的资源插件标签resources。如果我们不在POM文件中这样配置的话,最终的目标文件中是不会加载java目录下的XML配置文件的。之所以要配置是因为将它与dao接口放在了同一个目录下面,而dao接口是在java目录下面的。如果我们直接放在resources目录下,就不需要在POM文件中配置了。
多说一下:我们其实也可以将.yml文件,.yaml文件或者是.properties文件也放在java目录下面,这样为了能够使这些配置文件起作用,也需要在POM文件的resources标签下做一下配置。
4、建立一下数据库
项目中我使用的数据库是MySQL,版本是8.0.11的。
由于是刚开始使用,因此我在数据库ssm中只创建了一张数据表,表名为student,表结构如下所示:
下面是SQL脚本:
CREATE DATABASE IF NOT EXISTS `ssm` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */;
USE `ssm`;
CREATE TABLE IF NOT EXISTS `student` (
`id` int(11) NOT NULL COMMENT 'id作为主键',
`name` varchar(80) DEFAULT '' COMMENT '学生姓名',
`email` varchar(80) DEFAULT '' COMMENT '学生邮箱地址',
`age` int(11) DEFAULT NULL COMMENT '学生年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
DELETE FROM `student`;
/*!40000 ALTER TABLE `student` DISABLE KEYS */;
INSERT INTO `student` (`id`, `name`, `email`, `age`) VALUES
(1001, '马冬梅', '[email protected]', 25),
(1002, '郝建', '[email protected]', 21),
(1003, '马小', '[email protected]', 43),
(1004, '江小白', '[email protected]', 25);
/*!40000 ALTER TABLE `student` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
5、项目代码结构
这里我将我的代码结构直接截取一个图片展示一下:
6、POM文件
先粘贴一下我的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>com.dcy</groupId>
<artifactId>mybatis20220717First</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 引入MyBatis的jar包的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- MySQL驱动jar包的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<!-- 所在的目录-->
<directory>src/main/java</directory>
<includes><!-- 这个目录下的所有.xml,.properties文件都会被扫描加载到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
首先是依赖,1、我们使用了单元测试的依赖,也就是junit依赖。
2、引入了mybatis依赖,也就是引入了和mybatis有关的jar包。这里注意一下:引入的只有和mybatis有关的依赖和jar包,并没有和Spring整合。
3、引入了和MySQL驱动有关的jar包。
然后是<build>标签和里面的<resources>标签。这两个标签里面的内容是因为我们将MyBatis的mapper.xml文件放在了java目录下面,如果我们放在resources根目录下面,那么就不需要在POM文件中这样显示的标出来。如果我们不在POM文件中标出来,那么是加载扫描不到的,这样就肯定出错!!注意看我代码上面的注释!!
7、Student实体类
Student实体类是和表student对应的,代码如下:
package com.dcy.domain;
public class Student {
//这里我们的属性名和数据库表中的列名保持一致,如果不一致的话我们需要在mapper.XML文件中配置或者是使用注解配置
private Integer id;
private String name;
private String email;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "一个学生实体的信息{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
在上面的Student实体类的代码中,有四个属性,属性名和数据库表中的字段名保持了一致。如果不一致,我们是需要在mapper.XML中另外配置上的,或者是今后结合了Spring框架以后,需要使用注解配置上的。其实:很多时候是不一致的,我们在mapper.XML文件中配置上就可以了。
然后我们重写了toString()方法,重写的话我们可以直接直接输出某一个对象的信息。
8、dao层的StudentDao接口
dao层StudentDao接口的代码如下所示:
package com.dcy.dao;
import com.dcy.domain.Student;
public interface StudentDao {
//查询一个学生
public Student selectStudentById(Integer id);
}
其实:在我们这个项目中,这个接口根本就用不上!!但是,我们在实际的开发中,按照开发规范,因为结合了Spring后端开发框架,所以这个接口是必须有的,因此在这里我们也加上了。
接口比较简单,只有一个方法。
9、mapper.XML文件StudentDao.xml文件代码
这个配置文件可以有很多个,就是我们在POM文件中配置的,放在java目录下的配置文件。每一个dao层接口都对应这样的一个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.dcy.dao.StudentDao">
<!--
select标签表示的是查询操作,里面是查询语句。
id属性的值是SQL语句的唯一标识,是一个自定义的字符串。
按照编码规范,这里统一推荐使用dao接口中对应的方法名称。
resultType属性是表示执行SQL语句后,结果对应那个类型的JAVA对象。resultType的值是某个实体类的全限定名称。
比如:com.dcy.domain.Student
-->
<select id="selectStudentById" resultType="com.dcy.domain.Student">
select id,name,email,age from student where id=1001
</select>
</mapper>
<!--
最上面的四行代码是固定的。
1、http://mybatis.org/dtd/mybatis-3-mapper.dtd 这个是约束文件。
约束文件也就是规定了这个XML文件中可以使用那些标签,标签之间的嵌套顺序是什么,标签中可以有哪些属性。
这是一个约束文件的链接,我们可以在浏览器中访问,最后下载下来的。
2、<mapper>标签是根标签,其中的namespace属性是必须有的,不能为空,并且在整个项目中是唯一的。
推荐使用这个文件所对应Dao接口的全限定路径,也就是com.**.**.**这样的形式
它的作用是参与识别SQL语句的。
3、在mapper标签里面就可以写select,insert,update,delete标签,里面就写对应的SQL语句就可以了。
当然,里面还可以写resultMap标签,就是对应属性名和数据表列名的。这个后面详细说。
-->
注意看我代码中的注释:1、代码的前几行是文件的头,约束文件,包含这个配置文件中可以有哪些标签,标签之间的嵌套关系是什么,标签中有哪些属性,标签的先后顺序应该是什么等。
2、根标签<mapper>标签的namespace属性值在整个项目中是唯一的,虽然可以随便写,但是按照规范,我们写成对应dao接口的全限定路径。
3、mapper标签中写select,insert,update等标签,这些标签中写的就是SQL语句。这些标签统一都有id属性,id属性值在本文件中是唯一的。虽然可以乱写,但是按照规范需要是对应dao接口中的方法名。select标签还有一个resultType属性,这个属性表示查询后的返回值对应哪一个实体类。值是对应实体类的全限定路径名称。
10、MyBatis的总配置文件:mybatis.xml文件
除了上面每个dao层接口对应的mapper.XML配置文件以外,还需要有一个总的配置文件,这个配置文件按照规范,是必须放在resources目录下的。mybatis.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"></transactionManager>
<!-- 下面是配置一下数据源,其实后面我们就不这样配置了,都在.yml文件中配置-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 用于指定其它mapper.xml文件的位置,也就是路径
这个路径是从target/classes根路径开始的,也即是com/**/**/这样的
使用注意:使用 / 分隔路径;一个mapper标签指定一个文件;
-->
<mappers>
<mapper resource="com/dcy/dao/StudentDao.xml"></mapper>
</mappers>
</configuration>
注意看我代码中的注释:首先是一个environments标签,这个标签有一个属性是default,这个属性的值是environments标签里面包含的许多environment标签的id属性值。我们在实际的开发过程中,会经历开发,测试,试运行等几个阶段,每个阶段由于现实情况的限制,用到的数据库环境也不一样,因此这个标签中default就是我们默认使用下面许多数据库环境配置中的哪一个。下面的每一个environment标签都表示一个数据库环境配置。里面主要就是配置一下驱动,URL,用户名和数据库密码。
多说一下:我们在今后的实际开发中,都是将数据库的配置写在application文件中的,不会这样配置在XML文件中。上面的这种代码我们知道有这种方式,可以这样配置就可以了。以后真的用到了,再回头查这种写法就可以了。
下面是mappers标签,里面有很多个mapper标签。每一个mapper标签就表示一个dao层的XML配置文件。也就是说,我们需要将每一个dao层的XML配置文件都在这个总配置文件里面声明一下。mapper标签里面有一个resource属性,属性值是:这个dao层XML配置文件的根路径地址。无论这个dao层XML配置文件在不在resources目录下,都需要在这里声明,并且从类路径下开始写文件路径(如果在java文件夹下,那就是:com/**/**.xml 如果在resources文件夹下,那就是**/**.xml )。
11、Main方法中的测试
先粘贴一下代码,如下所示:
package com.dcy;
import com.dcy.domain.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class Starter {
public static void main(String[] args) throws IOException {
/*
myBatis的核心类是SqlSessionFactory
就是需要我们加载一下主配置文件,有点像我们使用SpringMVC加载spring.xml配置文件或者BeanFactory对象一样。
这些在官网是有的。
*/
//定义主配置文件的目录,从类路径开始:com/**/**这样的,如果是在resources目录下,那就是 **/**/
String config="mybatis.xml";
//下面读取主配置文件,使用MyBatis框架中的Resources类
InputStream inputStream = Resources.getResourceAsStream(config);
//下面创建SqlSessionFactory对象,使用的是SqlSessionFactoryBuilder类,需要用到上面的inputStream参数
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//MyBatis最终执行SQL语句,使用的是SqlSession类中的方法。SqlSession实例的获得,依靠的是SqlSessionFactory类。
//下面获取SqlSession对象,SqlSession是接口
SqlSession session = sqlSessionFactory.openSession();
//指定要执行的SQL语句的id
//SQL语句的id=namespace+"."+SQL语句所在标签的id属性的值
String SQLID="com.dcy.dao.StudentDao"+"."+"selectStudentById";
//通过SqlSession对象的方法执行SQL语句
Student student = session.selectOne(SQLID);
System.out.println(student);
//最后我们关闭SqlSession对象
session.close();
}
}
上面的代码看起来比较繁杂,不容易理解。但这就是使用MyBatis所必须的。其中有些代码行是固定化的,我们可以从官网上看到,下面我截一张官网的图:
官网剩下的我就不截取了,其实就是给我们讲必须有的步骤,并且给了代码例子。
我下面解释一下我上面的代码(其实也是跟官网上来的,我代码里面也有注释):
先说一下,我们通过MyBatis里面的SqlSession接口的某一个方法来执行XML文件里的SQL语句。这样就需要我们能够创建SqlSession对象。MyBatis又让我们使用SqlSessionFactory类型对象的某一个方法得到SqlSession对象。由于SqlSessionFactory也是接口类型,因此MyBatis让我们使用SqlSessionFactoryBuilder类的对象的某一个方法来创建这个接口。是不是很绕!!,我是不理解为什么工厂模式的使用会这么绕!
1、我们首先需要加载一下总配置文件。先使用一个String类型存储总配置文件的路径,还是总类路径开始。然后使用ibatis包下面的Resources类的静态方法getResourceAsStream()方法,将这个字符串形式的路径作为参数,传递进去。得到一个InputStream类型的对象。InputStream类来自于java自身。
2、我们new一个SqlSessionFactoryBuilder对象,调用这个对象的build方法,将上面的InputStream对象作为参数(也就是总配置文件),得到了一个SqlSessionFactory接口的对象。
3、然后使用这个SqlSessionFactory接口的对象的openSession()方法,得到一个SqlSession接口的对象。我们就可以使用这个SqlSession接口的对象的某些方法来执行SQL语句了。
4、我们要组装一下我们想执行的SQL语句的地址。因为每一个dao层的XML配置文件的mapper标签都有一个namespace属性,这个属性的值在全项目中是唯一的。而配置文件中,每一个SQL语句所在的标签的id属性值又是唯一的。这样一来:项目中每一条SQL语句都有一个唯一的“地址”。我们要做的就是提供这个SQL语句的“地址”,也就是:namespace属性值+“.”+id属性值。
5、将SQL语句的地址作为参数传给SqlSession类型对象的某一个方法,然后执行方法并得到对应的查询结果。(具体要执行那个方法,我们自己看方法名应该就知道了)
6、关闭这个SqlSession对象。
以上就是全部的执行过程,看似很复杂,其实很多都是固定的步骤。
12、执行结果
上面Main方法的执行结果如下所示:
13、说一下使用MyBatis执行SQL语句的过程代码
像是我们Main方法中测试的一样,过程其实还是很繁琐的,初学者还不理解。其实后面真正开发中,后端我们使用Spring框架的时候,我们可以引入两者结合连接的jar包,在jar包里面已经给我们封装好了这个过程,不需要我们自己手敲类似Main方法里面的代码。甚至就算我们不使用两个框架结合连接的jar包,我们可以自己写一个类将这些固定的代码封装起来,形成一个工具类!!
上面的代码过程作为一个了解。后面真正的了解MyBatis的内部设计方式,原理,可以等更加资深以后。