目录
Maven是专门用于管理和构建Java项目的工具。
Maven是专门用于管理和构建Java项目的工具,它的主要功能有:
提供了一套标准化的项目结构
每一个开发工具(IDE)都有自己不同的项目结构,它们互相之间不通用。
而Maven提供了一套标准化的项目结构,所有的IDE使用Maven构建的项目完全一样,所以IDE创建的Maven项目可以通用。

提供了一套标准化的构建流程(编译,测试,打包,发布……)

提供了一套依赖管理机制
Maven使用标准的坐标配置来管理各种依赖, 只需要简单的配置就可以完成依赖管理:

项目对象模型pom (Project Object Model)
依赖管理模型(Dependency)
插件(Plugin)

项目对象模型pom就是将我们自己抽象成一个对象模型,有自己专属的坐标,是唯一标识。
项目对象模型pom通过xml格式保存的pom.xml文件。该文件用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。
如下图所示是一个Maven项目:
依赖管理模型则是使用坐标来描述当前项目依赖哪些第三方jar包,如下图所示:

项目管理模型和依赖管理模型结合起来,体现Maven方便的依赖管理。
构建生命周期和插件部分用来完成 标准化构建流程 。
如我们需要编译,Maven提供了一个编译插件供我们使用。我们需要打包,Maven就提供了一个打包插件提供我们使用等。
Maven用坐标配置管理依赖。
依赖jar包则其实存储在本地仓库中,项目运行时从本地仓库中拿需要的依赖jar包。
本地仓库:自己计算机上的一个目录
中央仓库:由Maven团队维护的全球唯一的仓库
远程仓库(私服):一般由公司团队搭建的私有仓库
当项目中使用坐标引入对应依赖jar包后,首先会查找本地仓库中是否有对应的jar包:
如果有,则在项目直接引用;
如果没有,则去中央仓库中下载对应的jar包到本地仓库。

如果还可以搭建远程仓库,将来jar包的查找顺序则变为:
本地仓库 --> 远程仓库--> 中央仓库

什么是坐标?
Maven 中的坐标是资源的唯一标识
使用坐标来定义项目或引入项目中需要的依赖
Maven 坐标主要组成
groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)
artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
version:定义当前项目版本号
如下图就是使用坐标表示一个项目:

注意:
上面所说的资源可以是插件、依赖、当前项目。
我们的项目如果被其他的项目依赖时,也是需要坐标来引入的。
1.下载
官方下载链接:https://maven.apache.org/download.cgi
Binary是可执行版本,已经编译好可以直接使用。
Source是源代码版本,需要自己编译成可执行软件才可使用。
我们选择已经编译好的windows可执行版本进行安装:选择zip版本(linux选择tar.gz)
2.配置环境变量:


3.配置本地仓库
修改 D:\apache-maven-3.8.6\conf/settings.xml 中的

默认使用jdk1.4,太老

将下面代码加到
- <profile>
- <id>jdk-11id>
- <activation>
- <activeByDefault>trueactiveByDefault>
- <jdk>11jdk>
- activation>
- <properties>
- <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
- <maven.compiler.source>11maven.compiler.source>
- <maven.compiler.target>11maven.compiler.target>
- properties>
- profile>
4.配置阿里云私服:
中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包。
修改 conf/settings.xml 中的
- <mirror>
- <id>alimavenid>
- <name>aliyun mavenname>
- <url>http://maven.aliyun.com/nexus/content/groups/public/url>
- <mirrorOf>centralmirrorOf>
- mirror>
compile :编译,生成target目录
clean:清理,删除target目录
test:测试,执行test文件下测试代码
package:打包,将当前项目打包成的jar包
install:安装,将当前项目打成jar包,并安装到本地仓库

编译命令演示:
命令行运行mvn compile。
首次编译会先从阿里云(之前conf/settings.xml配置了阿里云私服)下载编译需要插件的jar包,在本地仓库(之前conf/settings.xml配置了本地仓库位置)也能看到下载好的插件。

在项目下会生成一个 target 目录,里面保存编译后的字节码文件。

清理命令演示:
mvn clean
执行上述命令可以看到
从阿里云下载清理需要的插件jar包
删除项目下的 target 目录
Maven 构建项目生命周期描述的是一次构建过程经历经历了多少个事件
Maven 对项目构建的生命周期划分为3套:
clean :清理工作。
default :核心工作,例如编译,测试,打包,安装等。
site : 产生报告,发布站点等。这套生命周期一般不会使用。
同一套生命周期内,执行后边的命令,前面的所有命令会自动执行。
默认生命周期:


设置 IDEA 使用本地安装的 Maven,并修改配置文件路径,记得apply:

apply再ok。
file-new module-选择构建Maven、jdk版本最好大于1.5:
也可以先创建空项目,在项目内创建Maven模块。
下面生成器Maven archetype(原型)是创建Maven web的,在下下一篇讲Tomcat时会用到

填写模块名称,坐标信息,点击finish,创建完成

创建好的项目目录结构如下:

然后在java文件夹下建包,创建类
导入Maven项目:open-双击项目的pom.xml文件即可
Maven-Helper可以右键Maven项目,直接run Maven和调试。
File --> Settings--Plugins--搜索 Maven,选择第一个 Maven Helper,点击Install安装,弹出面板中点击Accept--重启

打包时出现编码错误:
彻底解决:全部编码设置成utf-8

单文件解决:
在pom.xml配置如下代码,适用于项目要在不同项目移动:
- <properties>
- <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
- <maven.compiler.encoding>UTF-8maven.compiler.encoding>
- <java.version>11java.version>
- <maven.compiler.source>11maven.compiler.source>
- <maven.compiler.target>11maven.compiler.target>
- properties>
使用坐标引入jar包
1.在项目的 pom.xml 中编写
2.在
3.定义坐标的 groupId,artifactId,version
groupId:定义当前Maven项目隶属的实际项目。
例如org.sonatype.nexus,此id前半部分org.sonatype代表此项目隶属的组织或公司,后部分代表项目的名称。
如果此项目多模块话开发的话就子模块可以分为org.sonatype.nexus.plugins和org.sonatype.nexus.utils等。
特别注意的是groupId不应该对应项目隶属的组织或公司,也就是说groupId不能只有org.sonatype而没有nexus。
artifactId是构件ID,该元素定义实际项目中的一个Maven项目或者是子模块,如上面官方约定中所说,构建名称必须小写字母,没有其他的特殊字符,推荐使用“实际项目名称-模块名称”的方式定义,例如:spirng-mvn、spring-core等。

点击刷新按钮,使坐标生效

注意:
具体的坐标我们可以到如下网站进行搜索
设置自动刷新:
File --> Settings---Build Tools---any changes

快捷方式引入jar包
1.在 pom.xml 中 按 alt + insert,选择 Dependency

2.在弹出的面板中搜索对应坐标,然后双击选中对应坐标

3.点击刷新按钮,使坐标生效
通过设置坐标的依赖范围(scope),可以设置 对应jar包的作用范围:编译环境、测试环境、运行环境。
如下图所示给 junit 依赖通过 scope 标签指定依赖的作用范围。 那么这个依赖就只能作用在测试环境,其他环境下不能使用。

依赖范围scope可取值:
| 依赖范围 | 编译classpath | 测试classpath | 运行classpath | 例子 |
|---|---|---|---|---|
| compile | Y | Y | Y | logback |
| test | - | Y | - | Junit |
| provided | Y | Y | - | servlet-api |
| runtime | - | Y | Y | jdbc驱动 |
| system | Y | Y | - | 存储在本地的jar包 |
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github
持久层:
负责将数据保存到数据库的那一层代码。
以后开发我们会将操作数据库的Java代码作为持久层。而Mybatis就是对jdbc代码进行了封装。
JavaEE三层架构:表现层做页面展示、业务层做逻辑处理、持久层对数据持久化
下图是持久层框架的使用占比。

框架:
框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
JDBC 缺点
下面是 JDBC 代码,我们通过该代码分析都存在什么缺点:

硬编码
注册驱动、获取连接
上图标1的代码有很多字符串,而这些是连接数据库的四个基本信息,以后如果要将Mysql数据库换成其他的关系型数据库的话,这四个地方都需要修改,如果放在此处就意味着要修改我们的源代码。
SQL语句
上图标2的代码。如果表结构发生变化,SQL语句就要进行更改。这也不方便后期的维护。
操作繁琐
手动设置参数
手动封装结果集
上图标4的代码是对查询到的数据进行封装,而这部分代码是没有什么技术含量,而且特别耗费时间的。
Mybatis 优化
硬编码可以配置到配置文件
操作繁琐的地方mybatis都自动完成
如图所示

核心配置文件标签必须有向后顺序(相比之下,SQL映射配置文件的同级标签没有先后顺序),如下:

所有子标签均需书写在当前根标签内部
示例:
- configuration
- PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
- <typeAliases>
- <package name="package1.pojo"/>
- 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:///mybatis?useSSL=false"/>
- <property name="username" value="root"/>
- <property name="password" value="1234"/>
- dataSource>
- environment>
- environments>
- <mappers>
-
- <package name="package1.mapper"/>
- mappers>
- configuration>
作用:将数据库配置属性从dataSource标签内部提取到外部
属性:
resource:设置外部属性文件类路径
url:设置外部属性文件真实路径
示例:
- <properties resource="druid.properties">properties>
-
-
- <environments default="development">
- <environment id="development">
-
- <transactionManager type="JDBC"/>
-
- <dataSource type="POOLED">
- <property name="driver" value="${driverClassName}"/>
- <property name="url" value="${url}"/>
- <property name="username" value="${username}"/>
- <property name="password" value="${password}"/>
- dataSource>
- environment>
- environments>
作用: 是mybatis中极为重要的调整设置,他们会改变mybatis的运行时行为
mapUnderscoreToCamelCase:开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
默认值为false,当设置为true时开启驼峰命名
注意:只能将a_bc与aBc自动映射,不能将a_b与aBc自动映射
示例代码
- <settings>
-
- <setting name="mapUnderscoreToCamelCase" value="true"/>
- settings>
alias译为别名
作用 :类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
自定义别名
- <typeAliases>
- <typeAlias alias="Author" type="domain.blog.Author"/>
- <typeAlias alias="Blog" type="domain.blog.Blog"/>
- <typeAlias alias="Comment" type="domain.blog.Comment"/>
- <typeAlias alias="Post" type="domain.blog.Post"/>
- <typeAlias alias="Section" type="domain.blog.Section"/>
- <typeAlias alias="Tag" type="domain.blog.Tag"/>
- typeAliases>
也可以指定一个包名进行扫描,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
- <typeAliases>
- <package name="package1.pojo"/>
- typeAliases>
这样sql映射文件就能省略路径package1.pojo:
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="package1.mapper.UserMapper">
- <select id="selectAll" resultType="package1.pojo.User">
- select * from tb_user;
- select>
- mapper>
作用:配置数据库连接环境信息。
- 可以配置多个environment。
- 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。这样通过default属性切换不同的environment环境。
- 尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
environments属性default是每个environment默认使用的环境 ID
environment属性id是指定该 environment 元素定义的环境 ID。
事务管理器transactionManager属性type有两种:
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
数据源dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源,type有三种:
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。 JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
示例:
-
- <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:///mybatis?useSSL=false"/>
- <property name="username" value="root"/>
- <property name="password" value="1234"/>
- dataSource>
- environment>
- environments>
作用:加载映射文件
- <mappers>
-
- <mapper resource="UserMapper.xml"/>
- mappers>
需求:查询user表中所有的数据

1.创建user表,添加数据
- create database mybatis;
- use mybatis;
-
- drop table if exists tb_user;
-
- create table tb_user(
- id int primary key auto_increment,
- username varchar(20),
- password varchar(20),
- gender char(1),
- addr varchar(30)
- );
-
- INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
- INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
- INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
2.创建模块,导入坐标
在创建好的模块中的 pom.xml 配置文件中添加依赖的坐标:
- <dependencies>
-
- <dependency>
- <groupId>org.mybatisgroupId>
- <artifactId>mybatisartifactId>
- <version>3.5.5version>
- dependency>
-
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <version>5.1.46version>
- dependency>
-
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.13version>
- <scope>testscope>
- dependency>
-
-
- <dependency>
- <groupId>org.slf4jgroupId>
- <artifactId>slf4j-apiartifactId>
- <version>1.7.20version>
- dependency>
-
- <dependency>
- <groupId>ch.qos.logbackgroupId>
- <artifactId>logback-classicartifactId>
- <version>1.2.3version>
- dependency>
-
- <dependency>
- <groupId>ch.qos.logbackgroupId>
- <artifactId>logback-coreartifactId>
- <version>1.2.3version>
- dependency>
- dependencies>
注意:需要在项目的 resources 目录下创建logback的配置文件
- <configuration>
-
- <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>[%level] %cyan([%thread]) %boldGreen(%logger{15}) - %msg %npattern>
- encoder>
- appender>
- <logger name="package1" level="DEBUG" additivity="false">
- <appender-ref ref="Console"/>
- logger>
-
-
-
- <root level="DEBUG">
- <appender-ref ref="Console"/>
- root>
- configuration>
slf4j,simple logging facade for java的缩写,翻译为java的简单日志外观。slf4j是一个开源项目,它提供我们一个一致的API来使用不同的日志框架,比如: java.util.logging,logback,log4j等。slf4j使用户可以在运行时嵌入他们想使用的日志框架。从名字中可以看出,它其实使用的是facade设计模式来实现的。
Logback是SpringBoot内置的日志处理框架,你会发现spring-boot-starter其中包含了spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 logback。官方文档:http://logback.qos.ch/manual/
3.编写 MyBatis 核心配置文件 -- > 替换连接信息 解决硬编码问题
在模块下的 resources 目录下创建mybatis的配置文件 mybatis-config.xml,内容如下:
- configuration
- PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
- <typeAliases>
- <package name="package1.pojo"/>
- 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:///mybatis?useSSL=false"/>
- <property name="username" value="root"/>
- <property name="password" value="1234"/>
- dataSource>
- environment>
- environments>
- <mappers>
-
- <mapper resource="UserMapper.xml"/>
- mappers>
- configuration>
4.编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题
在模块的 resources 目录下创建映射配置文件 UserMapper.xml,内容如下:
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="test">
- <select id="selectAll" resultType="package1.pojo.User">
- select * from tb_user;
- select>
- mapper>
另外,可通过resultMap实现对数据库列名起别名,以解决数据库列名和User类属性命名法不对应问题(数据库标识符不区分大小写,采用下划线命名法,java标识符常用驼峰命名法):
- <resultMap id="userResultMap" type="user">
- <result column="username" property="userName"/>
- <result column="password" property="passWord"/>
- resultMap>
- <select id="selectAll" resultMap="userResultMap">
- select * from tb_user;
- select>
根标签【mapper】
作用:所用子标签均需书写在mapper内部
namespace与接口全路径类名【类的全限定名】一致
八大子标签:
insert:定义增加SQL语句
delete:定义删除SQL语句
update:定义修改语句
select:定义查询SQL语句
sql:定义SQL语句块
resultMap:定义结果集映射【resultType解决不了时,使用resultMap】
cache:定义缓冲类
cache-ref:定义引用缓存
5.在package1.pojo包下创建 User类
POJO(Plain Old Java Objects)译为,简单的Java对象,其实就是没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的,没有遵从特定的Java对象模型、约定或框架(如EJB)的不受任何限制的java对象。
- package package1.pojo;
- //pojo译为,简单的Java对象.其实就是没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的,没有遵从特定的Java对象模型、约定或框架(如EJB)的不受任何限制的java对象。
-
- public class User {
- private int id;
- private String username;
- private String password;
- private String gender;
- private String addr;
-
- //省略了 setter 和 getter
-
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", gender='" + gender + '\'' +
- ", addr='" + addr + '\'' +
- '}';
- }
- }
6.在package1包下编写 MybatisDemo 测试类
步骤:
1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
session译为“会话”,factory译为“工厂”。
2. 获取SqlSession对象,用它来执行sql
3. 执行sql
4. 释放资源
示例:
不用记,整合Spring后就只剩mapper优化后的执行方法
List
- package package1;
- 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 package1.pojo.User;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
-
- public class Demo {
-
- public static void main(String[] args) throws IOException {
- //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
-
- //2. 获取SqlSession对象,用它来执行sql
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //3. 执行sql
- //参数是一个字符串,该字符串必须是映射配置文件的namespace.id
- List
users = sqlSession.selectList("test.selectAll"); - System.out.println(users);
- //4. 释放资源
- sqlSession.close();
- }
- }
对比一下jdbc:
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.Statement;
-
- /**
- * 简单版
- */
- public class Demo {
-
- public static void main(String[] args) throws Exception {
- //1. 注册驱动
-
- //Class.forName("com.mysql.jdbc.Driver");//通过反射获取Driver实现类对象,从而加载驱动,注册驱动语句DriverManager.registerDriver(driver)在Driver的静态代码块里做过了。
- //此句仅在mysql可省略,其他数据库不能省略
- //2. 获取连接
- String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
- String username = "root";
- String password = "1234";
- Connection conn = DriverManager.getConnection(url, username, password);
- //3. 定义sql
- String sql = "update student set age = 80 where name='xiaohua'";
- //4. 获取执行sql的对象 Statement
- Statement stmt = conn.createStatement();
- //5. 执行sql
- int count = stmt.executeUpdate(sql);//受影响的行数
- //6. 处理结果
- System.out.println(count);
- //7. 释放资源
- stmt.close();
- conn.close();
- }
- }
解决SQL映射文件的警告提示:
在入门案例映射配置文件中存在报红的情况。问题如下:

产生的原因:Idea和数据库没有建立连接,不识别表信息。但是大家一定要记住,它并不影响程序的执行。
解决方式:在Idea中配置MySQL数据库连接。
IDEA中配置MySQL数据库连接
点击IDEA右边框的 Database ,在展开的界面点击 + 选择 Data Source ,再选择 MySQL

在弹出的界面进行基本信息的填写

点击完成后就能看到数据库编译器界面
而此界面就和 navicat 工具一样可以进行数据库的操作。也可以编写SQL语句
另外,如果发现写表明时候没有提示:
打开File的settings,把SQL Dialects选项的右边那个None改为你的默认数据库,我这边之前默认的是Generic SQL,然后我把它改回了我使用的mysql,之后就会有提示了。

Mapper 代理方式的目的:
解决原生方式中的硬编码
简化后期执行SQL

1.定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。如下图放就可以,因为编译Maven后java和resources下的目录和文件都在同一个根目录下。
注意:在resources下建多层文件夹要用斜杠,例如aa/bb,不能aa.bb。如果直接建aa.bb,则只建了一个名为aa.bb的文件夹,而不是两个。


2.设置SQL映射文件的namespace属性为Mapper接口全限定名:
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="package1.mapper.UserMapper">
- <select id="selectAll" resultType="package1.pojo.User">
- select * from tb_user;
- select>
- mapper>
3.在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

4.Mapper代理开发
- package package1;
- 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 package1.mapper.UserMapper;
- import package1.pojo.User;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
-
- public class Demo {
-
- public static void main(String[] args) throws IOException {
- //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
-
- //2. 获取SqlSession对象,用它来执行sql
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //3. 执行sql
- //参数是一个字符串,该字符串必须是映射配置文件的namespace.id
- // List
users = sqlSession.selectList("test.selectAll"); - //3.1获取UserMapper接口的代理对象
- UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
- //3.2调用sql方法
- List
users = userMapper.selectAll(); - System.out.println(users);
- //4. 释放资源
- sqlSession.close();
- }
- }
注意:如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用在核心配置文件mybatis-config.xml中用包扫描的方式简化SQL映射文件的加载。示例:
- <mappers>
-
- <mapper resource="package1/mapper/UserMapper.xml"/>
- mappers>
简化成:
- <mappers>
-
- <package name="package1.mapper"/>
- mappers>
安装MybatisX插件:
Setting--Plugins--MybatisX
作用是通过小鸟图标,方便mapper接口和mapper配置文件之间代码的统一。
参数占位符:
参数占位符里的内容为传入的参数名,或者参数的成员变量名,或map的键名。
mybatis提供了两种参数占位符:
#{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。底层使用的是 PreparedStatement
${} :拼接SQL。底层使用的是 Statement,会存在SQL注入问题。
示例:
- <select id="selectById" resultMap="userResultMap">
- select * from tb_user where id=#{id};
- select>
特殊字符处理:
1.转义字符
XML中,需要转义的字符有:
(1)& &
(2)< <
(3)> >
(4)" "
(5)' '
2.CDATA区
- <select id="selectById" resultMap="userResultMap">
- select * from tb_user where id
- >
- ]]> #{id};
- select>
环境准备
-
数据库表(tb_brand)及数据准备
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1); -
实体类 Brand
在 com.itheima.pojo 包下创建 Brand 实体类。
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;
//省略 setter and getter。自己写时要补全这部分代码
} -
编写测试用例
测试代码需要在 test/java 目录下创建包及测试用例。项目结构如下:

给列起别名并查询所有
SQL语句下划线命名法的列名和java实体类驼峰命名法的成员变量不同,会导致数据库给成员变量无法赋值的问题。通过resultMap标签给数据库列名起别名,可以解决这个问题。
接口UserMapper.java定义抽象方法:
List selectAll();
UserMapper.xml
- mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
- <mapper namespace="com.itheima.mapper.BrandMapper">
-
- <resultMap id="brandResultMap" type="brand">
-
- <result column="brand_name" property="brandName"/>
- <result column="company_name" property="companyName"/>
- resultMap>
- <select id="selectAll" resultMap="brandResultMap">
- select *
- from tb_brand;
- select>
- mapper>
sql映射配置文件查询成功后通过测试类List brands = brandMapper.selectAll();获得所有对象组成的List列表。
单条件查询
UserMapper.java
User selectById(int id);
UserMapper.xml
-
- <select id="selectById" resultMap="userResultMap" >
- select * from tb_user where id=#{id};
- select>
多条件查询(三种传多参方法)
BrandMapper.java
三种传参方法:
①散装参数:使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位
- //BrandMapper.java
- List
selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- where status = #{status}
- and company_name like #{companyName}
- and brand_name like #{brandName}
- select>
- //Demo.java
- List
brands = brandMapper.selectByCondition(status, companyName, brandName);
②实体类封装参数:将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。
- //BrandMapper.java
- List
selectByCondition(Brand brand);
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- where status = #{status}
- and company_name like #{companyName}
- and brand_name like #{brandName}
- select>
- //Demo.java
- Brand brand = new Brand();
- brand.setStatus(status);
- brand.setCompanyName(companyName);
- brand.setBrandName(brandName);
③map集合:将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。
- //BrandMapper.java
- List
selectByCondition(Map map);
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- where status = #{status}
- and company_name like #{companyName}
- and brand_name like #{brandName}
- select>
- //Demo.java
- Map map = new HashMap();
- map.put("status" , status);
- map.put("companyName", companyName);
- map.put("brandName" , brandName);
- List
brands = brandMapper.selectByCondition(map); - System.out.println(brands);
BrandMapper.xml都是一样的
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- where status = #{status}
- and company_name like #{companyName}
- and brand_name like #{brandName}
- select>
测试类:
- @Test
- public void Demo() throws IOException {
- //接收参数
- int status = 1;
- String companyName = "华为";
- String brandName = "华为";
-
- // 处理参数
- companyName = "%" + companyName + "%";
- brandName = "%" + brandName + "%";
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
-
- //4. 执行方法
- //方式一 :接口方法参数使用 @Param 方式调用的方法
- //List
brands = brandMapper.selectByCondition(status, companyName, brandName); - //方式二 :接口方法参数是 实体类对象 方式调用的方法
- //封装对象
- /* Brand brand = new Brand();
- brand.setStatus(status);
- brand.setCompanyName(companyName);
- brand.setBrandName(brandName);*/
-
- //List
brands = brandMapper.selectByCondition(brand); -
- //方式三 :接口方法参数是 map集合对象 方式调用的方法
- Map map = new HashMap();
- map.put("status" , status);
- map.put("companyName", companyName);
- map.put("brandName" , brandName);
- List
brands = brandMapper.selectByCondition(map); - System.out.println(brands);
-
- //5. 释放资源
- sqlSession.close();
- }
上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的SQL语句就不能那样写,要使用动态SQL。
多条件动态查询(动态SQL)

动态SQL:SQL语句随着用户的输入或外部条件的变化而变化,称为动态SQL。
Mybatis关于动态SQL的标签:
-
if
-
choose (when, otherwise)
-
trim (where, set)
-
foreach
if标签的test 属性:逻辑表达式
为了防止出现SQL语句where 后直接跟and的情况,给所有条件加了and,并且where后跟了1=1.
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- where 1=1
- <if test="status != null">
- and status = #{status}
- if>
- <if test="companyName != null and companyName != '' ">
- and company_name like #{companyName}
- if>
- <if test="brandName != null and brandName != '' ">
- and brand_name like #{brandName}
- if>
- select>
where 标签作用:
-
替换where关键字
-
会动态的去掉第一个条件前的 and
-
如果所有的参数没有值则不加where关键字
示例:
- <select id="selectByCondition" resultMap="brandResultMap">
- select *
- from tb_brand
- <where>
- <if test="status != null">
- and status = #{status}
- if>
- <if test="companyName != null and companyName != '' ">
- and company_name like #{companyName}
- if>
- <if test="brandName != null and brandName != '' ">
- and brand_name like #{brandName}
- if>
- where>
- select>
单条件动态查询(动态SQL)
先选择,再输入查询。

choose(when,otherwise)标签类似于Java 中的switch语句。
代码:
- <select id="selectByConditionSingle" resultMap="brandResultMap">
- select *
- from tb_brand
- <where>
- <choose>
- <when test="status != null">
- status = #{status}
- when>
- <when test="companyName != null and companyName != '' ">
- company_name like #{companyName}
- when>
- <when test="brandName != null and brandName != ''">
- brand_name like #{brandName}
- when>
- choose>
- where>
- select>
- //BrandMapper.java
- List
selectByConditionSingle(Brand brand);
- //Demo.java
- @Test
- public void testSelectByConditionSingle() throws IOException {
- //接收参数
- int status = 1;
- String companyName = "华为";
- String brandName = "华为";
-
- // 处理参数
- companyName = "%" + companyName + "%";
- brandName = "%" + brandName + "%";
-
- //封装对象
- Brand brand = new Brand();
- //brand.setStatus(status);
- brand.setCompanyName(companyName);
- //brand.setBrandName(brandName);
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
- //4. 执行方法
- List
brands = brandMapper.selectByConditionSingle(brand); - System.out.println(brands);
-
- //5. 释放资源
- sqlSession.close();
- }
添加数据
- //BrandMapper.java
- /**
- * 添加,如果返回值为int,则返回改变的行数,也可void,通过异常了解添加成功或失败
- */
- int add(Brand brand);
- <insert id="add">
- insert into tb_brand (brand_name, company_name, ordered, description, status)
- values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
- insert>
- //测试类
- @Test
- public void testAdd() throws IOException {
- //接收参数
- int status = 1;
- String companyName = "波导手机";
- String brandName = "波导";
- String description = "手机中的战斗机";
- int ordered = 100;
-
- //封装对象
- Brand brand = new Brand();
- brand.setStatus(status);
- brand.setCompanyName(companyName);
- brand.setBrandName(brandName);
- brand.setDescription(description);
- brand.setOrdered(ordered);
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //SqlSession sqlSession = sqlSessionFactory.openSession(true); //设置自动提交事务,这种情况不需要手动提交事务了
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
- //4. 执行方法
-
- System.out.println(brandMapper.add(brand));
- //要手动提交,不然会回滚事务。如果前面sqlSessionFactory.openSession(true),就不用再提交事务了
- sqlSession.commit();
- //5. 释放资源
- sqlSession.close();
- }
Mybatis事务:

返回主键
在 insert 标签上添加如下属性:
-
useGeneratedKeys:是够获取自动增长的主键值。true表示获取
-
keyProperty :指定将获取到的主键值封装到哪儿个属性里
- <insert id="add" useGeneratedKeys="true" keyProperty="id">
- insert into tb_brand (brand_name, company_name, ordered, description, status)
- values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
- insert>
- brandMapper.add(brand);
- System.out.println(brand.getId());
修改全部字段
获取到修改后的数据和id,修改此id对应的一行数据。
BrandMapper 接口
- /**
- * 修改
- */
- void update(Brand brand);
BrandMapper.xml
- <update id="update">
- update tb_brand
- <set>
- <if test="brandName != null and brandName != ''">
- brand_name = #{brandName},
- if>
- <if test="companyName != null and companyName != ''">
- company_name = #{companyName},
- if>
- <if test="ordered != null">
- ordered = #{ordered},
- if>
- <if test="description != null and description != ''">
- description = #{description},
- if>
- <if test="status != null">
- status = #{status}
- if>
- set>
- where id = #{id};
- update>
MybatisTest类
- @Test
- public void testUpdate() throws IOException {
- //接收参数
- int status = 0;
- String companyName = "波导手机";
- String brandName = "波导";
- String description = "波导手机,手机中的战斗机";
- int ordered = 200;
- int id = 6;
-
- //封装对象
- Brand brand = new Brand();
- brand.setStatus(status);
- // brand.setCompanyName(companyName);
- // brand.setBrandName(brandName);
- // brand.setDescription(description);
- // brand.setOrdered(ordered);
- brand.setId(id);
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //SqlSession sqlSession = sqlSessionFactory.openSession(true);
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
- //4. 执行方法
- int count = brandMapper.update(brand);
- System.out.println(count);
- //提交事务
- sqlSession.commit();
- //5. 释放资源
- sqlSession.close();
- }
修改动态字段
在修改界面用户可能只修改部分属性, 所以加条件判断每个属性修改框用户有没有填写,用防止更新语句最后一行有逗号。

删除一行数据
BrandMapper 接口
- /**
- * 根据id删除
- */
- void deleteById(int id);
BrandMapper.xml
- <delete id="deleteById">
- delete from tb_brand where id = #{id};
- delete>
MybatisTest类
- @Test
- public void testDeleteById() throws IOException {
- //接收参数
- int id = 6;
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //SqlSession sqlSession = sqlSessionFactory.openSession(true);
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
- //4. 执行方法
- brandMapper.deleteById(id);
- //提交事务
- sqlSession.commit();
- //5. 释放资源
- sqlSession.close();
- }
批量删除
编写SQL时需要遍历数组来拼接SQL语句。Mybatis 提供了 foreach 标签供我们使用
foreach 标签
用来迭代任何可迭代的对象(如数组,集合)。
-
collection 属性:指定遍历的数组
-
mybatis会将数组参数,封装为一个Map集合。
-
默认:array = 数组,key是array而不是数组名,如collection="array"和void deleteByIds(int[] ids);
-
可以使用@Param注解改变map集合的默认key的名称为数组名,如collection="ids"和void deleteByIds(@Param("ids") int[] ids);
-
item 属性:本次迭代获取到的元素,如item="id"。
-
separator 属性:集合项迭代之间的分隔符。foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。如separator=","
-
open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次。如open="("
-
close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次。如close=")"
BrandMapper 接口
- /**
- * 批量删除
- */
- void deleteByIds(int[] ids);
BrandMapper.xml
- <delete id="deleteByIds">
- delete from tb_brand where id
- in
- <foreach collection="array" item="id" separator="," open="(" close=")">
- #{id}
- foreach>
- ;
- delete>
MybatisTest类
- @Test
- public void testDeleteByIds() throws IOException {
- //接收参数
- int[] ids = {5,7,8};
-
- //1. 获取SqlSessionFactory
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //2. 获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //SqlSession sqlSession = sqlSessionFactory.openSession(true);
- //3. 获取Mapper接口的代理对象
- BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
- //4. 执行方法
- brandMapper.deleteByIds(ids);
- //提交事务
- sqlSession.commit();
- //5. 释放资源
- sqlSession.close();
- }
Mybatis参数传递
Mybatis 接口方法中可以接收各种各样的参数,如下:
-
多个参数
-
单个参数:单个参数又可以是如下类型
-
POJO 类型
-
Map 集合类型
-
Collection 集合类型
-
List 集合类型
-
Array 类型
-
其他类型
1.10.1 多个参数
如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param 注解,那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。
User select(@Param("username") String username,@Param("password") String password);
我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:
-
以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
-
以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);
代码验证:
-
在 UserMapper 接口中定义如下方法
User select(String username,String password);
-
在 UserMapper.xml 映射配置文件中定义SQL
- <select id="select" resultType="user">
- select *
- from tb_user
- where
- username=#{arg0}
- and password=#{arg1}
- select>
或者
- <select id="select" resultType="user">
- select *
- from tb_user
- where
- username=#{param1}
- and password=#{param2}
- select>
运行代码结果如下

-
在映射配合文件的SQL语句中使用用 arg 开头的和 param 书写,代码的可读性会变的特别差,此时可以使用 @Param 注解。
在接口方法参数上使用 @Param 注解,Mybatis 会将 arg 开头的键名替换为对应注解的属性值。
代码验证:
-
在 UserMapper 接口中定义如下方法,在 username 参数前加上 @Param 注解
User select(@Param("username") String username, String password); Mybatis 在封装 Map 集合时,键名就会变成如下:
map.put("username",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
-
在 UserMapper.xml 映射配置文件中定义SQL
-
运行程序结果没有报错。而如果将 #{} 中的 username 还是写成 arg0
-
运行程序则可以看到错误

结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。
注解实现增删改查
使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发
- @Select(value = "select * from tb_user where id = #{id}")
- public User select(int id);
注意:
-
注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要再映射配置文件中书写对应的 statement
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
-
查询 :@Select
-
添加 :@Insert
-
修改 :@Update
-
删除 :@Delete
注意:在官方文档中 入门 中有这样的一段话:

所以,注解完成简单功能,配置文件完成复杂功能。
而我们之前写的动态 SQL 就是复杂的功能,如果用注解使用的话,就需要使用到 Mybatis 提供的SQL构建器来完成,而对应的代码如下:

上述代码将java代码和SQL语句融到了一块,使得代码的可读性大幅度降低。