【Mybatis】2—Mybatis基本用法
⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记链接👉https://github.com/A-BigTree/Code_Learning
⭐⭐⭐⭐⭐⭐
如果可以,麻烦各位看官顺手点个star~😊
如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆
文章目录
- 3 Mybatis基本用法
-
- 3.1 HelloWorld
-
- 3.1.1 物理建模
- 3.1.2 逻辑建模
-
- 创建Maven项目加入依赖
- 创建Java实体类
- 3.1.3 搭建框架环境
-
- 导入依赖
- 配置文件
- 3.1.4 测试代码
- 3.2 HelloWorld强化
- 3.3 给SQL语句传参
-
- 3.3.1 `#{}`形式
- 3.3.2 `${}`形式
- 3.4 数据输入
-
- 3.4.1 MyBatis总体机制概括
- 3.4.2 概括说明
- 3.4.3 单个简单类型参数
-
- 抽象方法声明
- SQL语句
- 3.4.4 实体类型参数
-
- 抽象方法声明
- SQL语句
- 3.4.5 零散的简单数据类型
-
- 抽象方法声明
- SQL语句
- 3.4.6 Map类型参数
-
- 抽象方法声明
- SQL语句
- 测试
- 使用场景
- 3.5 数据输出
-
- 3.5.1 返回单个简单类型数据
-
- 抽象方法
- SQL语句
- 3.5.2 返回实体类对象
-
- 抽象方法
- SQL语句
- 增加配置自动识别对应关系
- 3.5.3 返回Map类型
-
- 抽象方法
- SQL语句
- 3.5.4 返回List类型
-
- 抽象方法
- SQL语句
- 3.5.5 返回自增主键
-
- 返回场景
- SQL语句
- 不支持自增主键的数据库
- 3.5.6 使用resultMap
3 Mybatis基本用法
3.1 HelloWorld
3.1.1 物理建模
CREATE DATABASE `mybatis-example`;USE `mybatis-example`;CREATE TABLE `t_emp`(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id)
);INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);
3.1.2 逻辑建模
创建Maven项目加入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version><scope>compile</scope>
</dependency>
创建Java实体类
实体类是和现实世界中某一个具体或抽象的概念对应,是软件开发过程中,为了管理现实世界中的数据而设计的模型。 实体类的多个不同的叫法:
- domain:领域模型;
- entity:实体;
POJO
:Plain Old Java Object;- Java bean:一个Java类;
/*** 和数据库表 t_emp 对应的实体类* emp_id INT AUTO_INCREMENT* emp_name CHAR(100)* emp_salary DOUBLE(10,5)** Java 的实体类中,属性的类型不要使用基本数据类型,要使用包装类型。因为包装类型可以赋值为null,表示空,而基本数据类型不可以。*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {private Integer empId;private String empName;private Double empSalary;}
3.1.3 搭建框架环境
导入依赖
<!-- 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.12</version><scope>test</scope>
</dependency><!-- MySQL驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.3</version>
</dependency>
配置文件
Mybatis全局配置文件:
习惯上命名为 mybatis-config.xml
,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --><environments default="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environment id="development"><!-- Mybatis的内置的事务管理器 --><transactionManager type="JDBC"/><!-- 配置数据源 --><dataSource type="POOLED"><!-- 建立数据库连接的具体信息 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/><property name="username" value="root"/><property name="password" value="atguigu"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapper resource="mappers/EmployeeMapper.xml"/></mappers></configuration>
- 注意:配置文件存放的位置是
src/main/resources
目录下
Mybatis 映射配置文件:
相关概念:ORM(Object Relationship Mapping)对象关系映射。
- 对象:Java的实体类对象;
- 关系:关系型数据库;
- 映射:二者之间的对应关系;
下表列举的是最简单的单表映射(一个表和一个类):
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
注意:EmployeeMapper.xml
所在的目录要和mybatis-config.xml
中使用mapper
标签配置的一致。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- mapper是根标签,namespace属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper"><!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --><!-- resultType属性:指定封装查询结果的Java实体类的全类名 --><select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}</select>
</mapper>
3.1.4 测试代码
@Test
public void testSelectEmployee() throws IOException {// 1.创建SqlSessionFactory对象// ①声明Mybatis全局配置文件的路径String mybatisConfigFilePath = "mybatis-config.xml";// ②以输入流的形式加载Mybatis配置文件InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath);// ③基于读取Mybatis配置文件的输入流创建SqlSessionFactory对象SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.使用SqlSessionFactory对象开启一个会话SqlSession session = sessionFactory.openSession();// 3.根据Mapper配置文件的名称空间+SQL语句的id找到具体的SQL语句// 格式是:名称空间.SQL语句的idString statement = "com.atguigu.mybatis.dao.EmployeeMapper.selectEmployee";// 要传入SQL语句的参数Integer empId = 1;// 执行SQL语句Object result = session.selectOne(statement, empId);System.out.println("o = " + result);// 4.关闭SqlSessionsession.close();}
说明:
SqlSession
:代表Java程序和数据库之间的会话。(HttpSession
是Java程序和浏览器之间的会话);SqlSessionFactory
:是“生产”SqlSession
的“工厂”;- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象;
3.2 HelloWorld强化
3.2.1 加入日志
在Mybatis工作过程中,通过打印日志的方式,将要执行的SQL语句打印出来。
加入依赖
<!-- 日志 -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version>
</dependency>
加入配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true"><!-- 指定日志输出的位置,ConsoleAppender表示输出到控制台 --><appender name="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志输出的格式 --><!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 --><pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern><charset>UTF-8</charset></encoder></appender><!-- 设置全局日志级别。日志级别按顺序分别是:TRACE、DEBUG、INFO、WARN、ERROR --><!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --><root level="INFO"><!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender --><appender-ref ref="STDOUT" /></root><!-- 根据特殊需求指定局部日志级别 --><logger name="com.atguigu.mybatis" level="DEBUG" />
</configuration>
修改测试代码
这里我们仅仅修改查询结果的打印方式,为此需要做两件事:
- 在类上加
@Slf4j
注解; - 使用
Logger
对象打印数据;
if (result != null) {log.info(result.toString());
}
3.2.2 关联外部属性文件
在实际开发时,同一套代码往往会对应多个不同的具体服务器环境。使用的数据库连接参数也不同。为了更好的维护这些信息,我们建议把数据库连接信息提取到Mybatis全局配置文件外边。
创建属性文件
wechat.dev.driver=com.mysql.jdbc.Driver
wechat.dev.url=jdbc:mysql://192.168.198.100:3306/mybatis-example
wechat.dev.username=root
wechat.dev.password=atguiguwechat.test.driver=com.mysql.jdbc.Driver
wechat.test.url=jdbc:mysql://192.168.198.150:3306/mybatis-example
wechat.test.username=root
wechat.test.password=atguiguwechat.product.driver=com.mysql.jdbc.Driver
wechat.product.url=jdbc:mysql://192.168.198.200:3306/mybatis-example
wechat.product.username=root
wechat.product.password=atguigu
引入属性文件中的值
在Mybatis
全局配置文件中指定外部jdbc.properties
文件的位置。
<properties resource="jdbc.properties"/>
引用属性文件中的值
在需要具体属性值的时候使用${key}格式引用属性文件中的键
<dataSource type="POOLED"><!-- 建立数据库连接的具体信息(引用了外部属性文件中的数据) --><property name="driver" value="${wechat.dev.driver}"/><property name="url" value="${wechat.dev.url}"/><property name="username" value="${wechat.dev.username}"/><property name="password" value="${wechat.dev.password}"/>
</dataSource>
3.2.3 用上Mapper接口
Mybatis中的Mapper
接口相当于以前的Dao。但是区别在于,Mapper仅仅只是建接口即可,我们不需要提供实现类。该用法的思路如下图所示:
测试类中抽取代码
这一步和Mapper接口没关系,只是对代码本身的优化:
private SqlSession session;// junit会在每一个@Test方法前执行@Before方法@Beforepublic void init() throws IOException {session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")).openSession();}// junit会在每一个@Test方法后执行@After方法@Afterpublic void clear() {session.commit();session.close();}
声明Mapper接口
public interface EmployeeMapper {Employee selectEmployee(Integer empId);}
- 方法名和SQL的
id
一致; - 方法返回值和
resultType
一致; - 方法的参数和SQL的参数一致;
- 接口的全类名和映射配置文件的名称空间一致;
测试方法
@Test
public void testUsrMapperInterface() {// 1.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);// 2.调用EmployeeMapper接口的方法完成对数据库的操作Employee emp = employeeMapper.selectEmployee(1);// 3.打印查询结果if (emp != null)log.info(emp.toString());
}
3.3 给SQL语句传参
3.3.1 #{}
形式
Mybatis会将SQL语句中的#{}
转换为问号占位符。
3.3.2 ${}
形式
${}
形式传参,底层Mybatis做的是字符串拼接操作。
通常不会采用
${}
的方式传值。 一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用${}
。 结论:实际开发中,能用#{}
实现的,肯定不用${}
。
3.4 数据输入
3.4.1 MyBatis总体机制概括
3.4.2 概括说明
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:只包含一个值的数据类型
- 基本数据类型:int、byte、short、double、……
- 基本数据类型的包装类型:Integer、Character、Double、……
- 字符串类型:String
- 复杂类型:包含多个值的数据类型
- 实体类类型:Employee、Department、……
- 集合类型:List、Set、Map、……
- 数组类型:int[]、String[]、……
- 复合类型:List、实体类中包含集合……
3.4.3 单个简单类型参数
抽象方法声明
Employee selectEmployee(Integer empId);
SQL语句
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}</select>
单个简单类型参数,在#{}
中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
3.4.4 实体类型参数
抽象方法声明
int insertEmployee(Employee employee);
SQL语句
<insert id="insertEmployee">insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})</insert>
Mybatis会根据#{}
中传入的数据,加工成getXxx()
方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}
解析后的问号占位符这个位置。
3.4.5 零散的简单数据类型
抽象方法声明
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);
SQL语句
<update id="updateEmployee">update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}</update>
3.4.6 Map类型参数
抽象方法声明
int updateEmployeeByMap(Map<String, Object> paramMap);
SQL语句
<update id="updateEmployeeByMap">update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}</update>
测试
@Test
public void testUpdateEmpNameByMap() {EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);Map<String, Object> paramMap = new HashMap<>();paramMap.put("empSalaryKey", 999.99);paramMap.put("empIdKey", 5);int result = mapper.updateEmployeeByMap(paramMap);log.info("result = " + result);}
使用场景
有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
3.5 数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
- 查询操作的查询结果
3.5.1 返回单个简单类型数据
抽象方法
int selectEmpCount();
SQL语句
<select id="selectEmpCount" resultType="int">select count(*) from t_emp</select>
Mybatis内部给常用的数据类型设定了很多别名。 以int
类型为例,可以写的名称有:int
、integer
、Integer
、java.lang.Integer
、Int
、INT
、INTEGER
等等。
3.5.2 返回实体类对象
抽象方法
Employee selectEmployee(Integer empId);
SQL语句
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 --><!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}</select>
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
增加配置自动识别对应关系
在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
<!-- 在全局范围内对Mybatis进行配置 -->
<settings><!-- 具体配置 --><!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 --><!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 --><!-- 规则要求数据库表字段命名方式:单词_单词 --><!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 --><setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.5.3 返回Map类型
适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
抽象方法
Map<String,Object> selectEmpNameAndMaxSalary();
SQL语句
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">SELECTemp_name 员工姓名,emp_salary 员工工资,(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资FROM t_emp WHERE emp_salary=(SELECT MAX(emp_salary) FROM t_emp)</select>
3.5.4 返回List类型
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
抽象方法
List<Employee> selectAll();
SQL语句
<!-- List<Employee> selectAll(); --><select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp</select>
3.5.5 返回自增主键
返回场景
例如:保存订单信息。需要保存Order
对象和List<OrderItem>
。其中,OrderItem
对应的数据库表,包含一个外键,指向Order
对应表的主键。
在保存List<OrderItem>
的时候,需要使用下面的SQL:
insert into t_order_item(item_name,item_price,item_count,order_id) values(...)
这里需要用到的order_id,是在保存Order对象时,数据库表以自增方式产生的,需要特殊办法拿到这个自增的主键值。至于,为什么不能通过查询最大主键的方式解决这个问题,参考下图:
SQL语句
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">insert into t_emp(emp_name,emp_salary)values(#{empName},#{empSalary})</insert>
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回
不支持自增主键的数据库
而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用
<insert id="insertEmployee" parameterType="com.atguigu.mybatis.beans.Employee" databaseId="oracle"><selectKey order="BEFORE" keyProperty="id" resultType="integer">select employee_seq.nextval from dual </selectKey> insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})</insert>
或者是
<insert id="insertEmployee" parameterType="com.atguigu.mybatis.beans.Employee" databaseId="oracle"><selectKey order="AFTER" keyProperty="id" resultType="integer">select employee_seq.currval from dual </selectKey> insert into orcl_employee(id,last_name,email,gender) values(employee_seq.nextval,#{lastName},#{email},#{gender})</insert>
3.5.6 使用resultMap
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee"><!-- 使用id标签设置主键列和主键属性之间的对应关系 --><!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 --><id column="emp_id" property="empId"/><!-- 使用result标签设置普通字段和Java实体类属性之间的关系 --><result column="emp_name" property="empName"/><result column="emp_salary" property="empSalary"/></resultMap><!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}</select>