SSM 框架中的 MyBatis 框架,半自动化持久层框架,学习笔记。
这里不想下载也可以不下载,这边自己下载可以获取官方的英文文档,可以对照 MyBatis 中文网站来看看最新版中的对应配置或者文件是否进行了修改。
后面在 maven 中使用 mybatis 的时候会自动导包的。
下载官方网站:mybatis/mybatis-3
中文文档网站:MyBatis中文网
在下载完成并且解压之后,在文件夹下面找到这样的 pdf 文件,即为官方文档:
IDE:idea 2021.3.2
构建工具:maven 3.6.3
MySQL 版本:MySQL 8.0
MyBatis 版本:MyBatis 3.5.10
File | Settings | Build, Execution, Deployment | Build Tools | Maven
设置中,确认 maven 用的是我们自己下载的 maven 版本,以及我们自己设置的 maven 本地仓库;pom.xml
文件中设置 packing
标签,将打包方式设置为 jar 包;pom.xml
文件中将依赖复制粘贴进来,这里面主要包括 MyBatis 核心、junit 测试、MySQL 连接驱动、log4j 日志的包;<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.10version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.29version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
dependencies>
src/main/resources
包下面新建一个核心配置文件,一般命名为 mybatis-config.xml
;
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<typeAliases>
<package name="com.atguigu.mybatis.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.atguigu.mybatis.mapper"/>
mappers>
configuration>
解释:
!DOCTYPE
后面的标签是配置文件的根标签;在 src/main/resources
文件夹下创建关于数据库连接的配置文件 jdbc.properties
:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=abc123
注意:
jdbc.xxx
;properties
标签引入 mybatis 配置文件中。新建一个文件夹专门用来放置用到的 mapper 接口:
新建一个 mapper 接口:
mapper 接口中编写要用到的和数据库交互的方法:
新建一个放置 mapper 映射文件的文件夹:
注意:
com/atguigu/mybatis/mapper
这种方式。新建一个 mapper 映射文件:
映射文件中编写具体的 mapper 接口想要实现的方法的 sql 语句:
注意,MyBatis 中可以面向接口操作数据,要保证两个一致:
src\mian\java
包下的什么位置;src/main/resources
目录下加入 log4j.xml 的配置文件:
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>
<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>
test\java
包下面新建一个用来测试的 java 类:SqlSession sqlSession = sqlSessionFactory.openSession();
);sqlSession.getMapper
方法,当我们传进去一个方法的类对象的时候,这个方法能帮助我们获取这个类的实例化对象(代理模式:帮助我们返回一个接口的实现类对象);/**
* SqlSession默认不自动提交事务,若需要自动提交事务
* 可以使用SqlSessionFactory.openSession(true);
*/
@Test
public void testMyBatis() throws IOException {
//加载核心配置文件,以字节输入流的方式获取,因为是流的方式,所以要抛出异常
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取sqlSessionFactory,工厂模式:将我们创建对象的过程进行封装,直接提供想要的对象
// 通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//创建SqlSession对象,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
//SqlSession sqlSession = sqlSessionFactory.openSession();
//创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口对象,我们只有接口,没有实现类,
// getMapper当我们传进去一个方法的类对象的时候,这个方法能帮助我们获取这个类的实例化对象
// 代理模式:帮助我们返回一个接口的实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试功能
int result = mapper.insertUser();
//提交事务
//sqlSession.commit();
System.out.println("result:" + result);
}
解释:
SqlSession sqlSession = sqlSessionFactory.openSession(true);
表示 SqlSession 对象所操作的 sql 会进行自动提交;执行上述方法控制台打印的日志文件如下,配置文件没有问题:
这里配置文件了解即可,到时候需要用哪个标签,就从官方文档中去找:
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties" />
<typeAliases>
<package name="com.atguigu.mybatis.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.atguigu.mybatis.mapper"/>
mappers>
configuration>
File | Settings | Editor | File and Code Templates
中设置 MyBatis 核心配置文件模板:核心配置文件信息,注释内容可以删掉:
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties" />
<typeAliases>
<package name="com.atguigu.mybatis.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.atguigu.mybatis.mapper"/>
mappers>
configuration>
设置好之后,在 src/main/resources
文件夹下面新建核心配置文件如下图所示,点击之后可以一键创建:
File | Settings | Editor | File and Code Templates
中设置映射文件模板:mapper 映射文件内容:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
mapper>
因为上面没有指定创建的文件名称,所以这里点击创建新的映射文件之后还要有一步命名,已经指定了文件格式,所以这里加不加 .xml 后缀都可以:
resultType
或 resultMap
,用于设置实体类和数据库表的映射关系, mybatis 省了处理结果集的的过程,但是我们还要指定返回的结果集的类型:
typeAliases
标签时,即我们设置了以包为单位,将包下所有的类型设置默认的类型别名,这里映射的 id 可以改为 User 即类的类名(不区分大小写)resultType
或 resultMap
的时候,报错信息如下:Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'com.atguigu.mybatis.mapper.UserMapper.getUserById'. It's likely that neither a Result Type nor a Result Map was specified.
TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 8
${}
和 #{}
以任意的名称获取参数值,但是需要注意 ${}
的单引号问题;${}
需要手动设置单引号 ;#{}
在解析的时候会自动添加单引号。
<select id="getUserByUsername" resultType="User">
select * from t_user where username = '${username}'
select>
此时 MyBatis 会将这些参数放在一个 map 集合中,以两种方式进行存储:
因此只需要通过 #{}
和 ${}
以键的方式访问值即可,但是需要注意 ${}
的单引号问题。
<select id="checkLogin" resultType="User">
select * from t_user where username = '${param1}' and password = '${param2}'
select>
当我们使用其他参数来访问,比如说将其中访问的属性值改为 where username = #{username}
的时候,会报如下的错误:
Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
#{}
和 ${}
,以键的方式访问值即可,但是需要注意 ${}
的单引号问题;
<select id="checkLoginByMap" resultType="User">
select * from t_user where username = #{username} and password = #{password}
select>
测试类的编写:
//当有多个字面量的时候,可以将多个字面量封装进一个 map 集合中
@Test
public void testCheckLoginByMap() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("username", "admin");
map.put("password", "123");
User user = mapper.checkLoginByMap(map);
System.out.println(user);
}
#{}
和 ${}
以属性的方式访问属性值即可,但是需要注意 ${}
的单引号问题;
<insert id="insertUser">
insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
insert>
//测试参数是实体类参数
@Test
public void testInsertUser() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
int result = mapper.insertUser(new User(null, "李四", "123", 23, "男", "[email protected]"));
System.out.println(result);
}
#{}
和 ${}
以键的方式访问值即可,但是需要注意 ${}
的单引号问题
<select id="checkLoginByParam" resultType="User">
select * from t_user where username = #{username} and password = #{password}
select>
@Test
public void testCheckLoginByParam() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
User user = mapper.checkLoginByParam("admin", "123");
System.out.println(user);
}
为啥我们能将我们想要的 key 值,以及对应的 value 值设置进去呢?
2. mapper 映射的底层使用了代理模式,通过反射执行当前命令对应的方法
3. 当前命令中,name 对应的是要执行的 sql 语句(唯一标识:mapper 映射文件中 namespace + id),方法对应的是 select 方法,所以 switch - case 直接跳到 select 方法去执行对应的方法:
这边在 else 模块中,第一个方法是将 args 参数转换成 sql 要求的参数,点进这个方法看一下里面怎么写的?
再进入方法内部,这个方法是一个将注解设定的命名规则的名称设置成 map 容器的 key 值的方法
第一步:name 是一个排序的 map 容器,其中 0 号位置放了我们通过注解命名的第一个参数名称,1 号位置放置了我们通过注解命名的第二个参数名称:
第二步:又因为我们有参数注解而且参数注解的个数不为1,所以跳到 else 中执行,新建一个 map 容器用来放置 (键值,参数值);
第三步:遍历 names 这个 map 容器,取出其中第一个键值对;
第四步:将它的 value 值即放置的自定义的 username 注解名称当作键值,将 args[当前键值对的键值] 即 args[0] 当作 value 值放进 params 这个 map 容器中;
第五步:定义一个字符串 param1,这里的 1 表示的是遍历第一个键值对即 i = 0;
第六步:如果当前 names 这个 map 容器中没有包含当前的这个 param1 这个值(防止我们自己定义了,重复放置),那么我们就将(param1 ,args[0])这一个键值对放进 param 这个 map 容器中,也就是我们既可以用我们自己设定的 username 来访问对应的参数值,也可以用 param1 来访问对应的参数值。
第七步:i++,遍历 names 容器中的下一个键值对,重复执行 4 - 6;
第八步:最后将生成的 param 这个 map 容器返回,我们可以从中任意选择一个键值来访问对应的参数。
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写:
//测试根据id查询用户信息,用 实体类、list集合来接收都可以
@Test
public void testGetUserById(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(mapper.getUserById(5));
}
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写:
测试类的编写:
//测试单条数据放在 map 集合中
@Test
public void testGetUserByIdToMap(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(mapper.getUserByIdToMap(6));
}
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写:
测试类的编写:
//测试获取多个数据,只能通过 list 来接收
@Test
public void testGetAllUser(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(mapper.getAllUser());
}
@MapKey
注解,此时就可以将每条数据转换的 map 集合作为值,以某个字段的值作为键,放在同一个map集合中mapper 映射文件中 sql 语句的编写:
测试类的编写:
//测试多条数据用 map 集合来接收
@Test
public void testGetAllUserToMap(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(mapper.getAllUserToMap());
}
java.lang.Integer --> int,integer
int --> _int,_integer
Map --> map
String --> string
mapper 映射文件中 sql 语句的编写:
测试类的编写:
//测试得到的是单个数据,需要用的 java 中的常见数据类型来接收
@Test
public void testGetCount(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
System.out.println(mapper.getCount());
}