一、Mybatis框架
1.概述
-
mybatis是什么?有什么特点?
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它和数据库无关性较低。
- 什么是ORM?
Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指得是数据库中的关系模型,对象关系映射,指的就是Java对象和数据库的关系模型之间建立一种对应关系。比如:用一个Java的Student类,去对应数据库中的一张student表,一个Student对象就对应student表中的一行数据,类中的属性和表中的列一一对应。
- 为什么Mybatis是半自动的ORM框架?
用Mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射,就可以直接进行CRUD操作了。由于Mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialet),所以Mybatis的数据库无关性比较低。虽然Mybatis需要手写SQL,但相比JDBC,他提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发效率。
2.原理示意图
-
Mybatis配置
SQLMapConfig.xml,此文件作为Mybatis的全局配置文件,配置了Mybatis的运行环境等信息,此后会有详细介绍。mapper.xml文件即SQL映射文件,文件中配置了操作数据库的SQL语句。此文件需要在SQLMapConfig.xml中加载。
-
通过Mybatis环境等配置信息构造SqlSessionFactory,即会话工厂
-
有会话工厂(SqlSessionFactory)创建sqlSession即会话,操作数据库需要通过sqlSession进行。
-
Mybatis底层自定义了Executor执行器接口来操作数据库,Executor接口有两个实现,一个是基本执行器,一个是缓存执行器
-
Mybatis底层自定义了Executor执行器接口来操作数据库,Executor接口有两个实现,一个是基本执行器,一个是缓存执行器
-
Mapped Statement也是Mybatis一个底层封装对象,它包含了Mybatis配置信息以及SQL映射信息等。mapper.xml文件中一个SQL对应一个Maped Statement对象,SQL的id即是Mapped Statement的id。
-
Mapped Statement对SQL输入参数进行定义,包含HashMap、基本数据类型、POJO等,Executor通过Mapped Statement在执行SQL前,将输入的java对象映射到SQL中,输入参数映射就是在JDBC编程中对PreparedStatement设置参数。
-
Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo等,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果集的解析处理过程
二、入门程序
1.原生开发
1.在jdbc数据库中,创建一张tbl_employee表(d_id字段暂时用不到)和tbl_dept表(本阶段暂时用不到此表)
2.打开IDEA,创建一个maven项目
3.导入依赖jar包
<?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.wangsuhang</groupId>
<artifactId>MyBatis3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<!--静态资源导出问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.创建一个pojo类(一个映射类)
package com.wangsuhang.mybatis_01_helloworld.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author suahng
* @date 2021-07-06 9:56
* @dec
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
}
5.编写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">
<!--
namespace: 名称空间;指定为接口的全类名(第二种方式这样写)
id:唯一标识,推荐使用mapper中接口的 具体 方法名
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
-->
<mapper namespace="com.wangsuhang.mybatis_01_helloworld">
<select id="getEmpById" resultType="com.wangsuhang.mybatis_01_helloworld.pojo.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
6.编写全局配置文件SQLMapConfig.xml( mybatis-config.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>
<!--settings:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--设置驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--sql mapper(SQL映射文件)的位置-->
<mappers>
<!--就是上一步的mappe.xml文件-->
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
7.测试代码和运行结果
package com.wangsuhang.mybatis_01_helloworld.test;
import com.wangsuhang.mybatis_01_helloworld.mapper.EmployeeMapper;
import com.wangsuhang.mybatis_01_helloworld.pojo.Employee;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisTest {
/**
* 1.根据xml配置文件(全局配置文件 mybatis-config.xml )创建一个 SqlSessionFactory 对象
* 有数据源的一些信息
* 2.sql映射文件(EmployeeMapper2.xml):配置了每一个sql,以及 sql 的封装规则
* 3.将 sql 映射文件注册在全局配置文件中
* 4.在测试类中写具体的代码:
* 1)根据全局配置文件得到 SQLSessionFactory
* 2)使用 SQLSession工厂,获取 sqlSession 对象,使用他来执行增删改查
* 一个 sqlSession 就是代表和数据库的一个会话,用完关闭
* 3)使用 sql 的唯一标识来告诉 MyBatis执行哪个SQL。 sql都是保存在sql映射文件中
*/
@Test
public void testMyBatis() throws IOException {
//1.获取sqlSessionFactory对象
String config = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(config);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
System.out.println(sqlSessionFactory);
//2.获取 sqlSession 实例,能直接执行已经映射的 sql 语句
SqlSession sqlSession = sqlSessionFactory.openSession();
//sql的唯一标识:statement- Unique identifier matching the statement to use.
//即mapper.xml文件中的namespace + SQL语句的id
//执行SQL要用的参数:parameter- A parameter object to pass to the statement.
Employee employee = sqlSession.selectOne("com.wangsuhang.mybatis_01_helloworld.getEmpById", 1);
System.out.println(employee);
//关闭资源
sqlSession.close();
}
}
运行结果
com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.wangsuhang.mybatis_01_helloworld.test.MyBatisTest,testMyBatis
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@f6c48ac
Opening JDBC Connection
Created connection 81009902.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d41cee]
==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ?
==> Parameters: 1(Integer)
<== Columns: id, lastName, email, gender
<== Row: 1, admin, jerry@qq.com, 女
<== Total: 1
Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d41cee]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d41cee]
Returned connection 81009902 to pool.
Process finished with exit code 0
2.Mapper动态代理方式开发DAO
1.实现原理
Mapper接口开发放肆只需要程序员编写Mapper接口(相当于DAO接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同DAO接口的实现类方法。
Mapper接口开发需要遵循以下规范:
- mapper.xml文件中namespace与mapper的全类名相同。
- Mapper接口方法名和mapper.xml中定义的每个statement(即具体的SQL语句)的id相同。
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
- Mapper接口方法的输出类型参数类型和mapper.xml中定义的每个resultType的类型相同。
2.改写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">
<!--
namespace: 名称空间;指定为接口的全类名
id:唯一标识,推荐使用mapper中接口的 具体 方法名
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
-->
<mapper namespace="com.wangsuhang.mybatis_01_helloworld.mapper.EmployeeMapper">
<select id="getEmpById" resultType="com.wangsuhang.mybatis_01_helloworld.pojo.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
3.编写XxxMapper.java(接口文件)
package com.wangsuhang.mybatis_01_helloworld.mapper;
import com.wangsuhang.mybatis_01_helloworld.pojo.Employee;
/**
* @author suahng
* @date 2021-07-06 14:18
* @dec
*/
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
4.测试代码和运行结果
package com.wangsuhang.mybatis_01_helloworld.test;
import com.wangsuhang.mybatis_01_helloworld.mapper.EmployeeMapper;
import com.wangsuhang.mybatis_01_helloworld.pojo.Employee;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @author suahng
* @date 2021-07-06 10:49
* @dec
*
* 1.接口式编程
* 原生: Dao ====> DaoImpl
* mybatis: Mapper ====> xxMapper.xml
* 2.SqlSession:代表和数据库的一次会话,用完必须关闭
* 3.SqlSession和Connection 一样它都是非线程安全。每次使用都应该获取新的对象
* 4.mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象
* (将接口和xml进行绑定)
* EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
* 5.两个重要的配置文件:
* mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息的那个...系统运行环境信息
* sql映射文件:保存了每一个sql语句的映射信息,将sql抽取了出来了
*/
public class MyBatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String config = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(config);
return new SqlSessionFactoryBuilder().build(is);
}
/**
* 1.获取sqlSessionFactory对象
* 解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSessionFactory
* 注意:MappedStatement:代表一个增删改查的详细信息
* 2.获取SQLSession对象
* 返回一个DefaultSqlSession对象,包含Executor和Configuration
* 这一步会创建Executor对象;
* 3.获取接口的代理对象(MapperProxy)
* getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
* 代理对象里面包含了,DefaultSqlSession(Executor)
*
* 4.执行增删改操作
*
*
* 总结:
* 1.根据配置文件(全局,sql映射)初试化出Configuration对象
* 2.创建一个DefaultSqlSession对象,
* 他里面包含Configuration以及
* Executor(根据全局配置文件的defaultExecutorType创建出对应的Executor)
* 3.DefaultSqlSession.getMapper(),拿到Mapper接口对应的MapperProxy
* 4.MapperProxy里面有(DefaultSqlSession);
* 5.执行增删改查方法:
* 1)调用DefaultSession的增删改查(Executor);
* 2)会创建一个StatementHandler对象;(同时也会创建出ParameterHandler和ResultSetHandler)
* 3)调用StatementHandler预编译参数以及设置参数值;
* 使用ParameterHandler来给sql设置参数
* 4)调用StatementHandler的增删改查方法;
* 5)ResultSetHandler封装结果
* 注意:
* 四大对象(Executor,ParameterHandler,ResultSetHandler,StatementHandler)
* 每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler)
*
* @throws IOException
*/
@Test
public void test01() throws IOException {
//1.获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//3.获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
System.out.println(mapper.getClass());
Employee emp = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(emp);
}finally {
//4.关闭资源
sqlSession.close();
}
}
}
运行结果
com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.wangsuhang.mybatis_01_helloworld.test.MyBatisTest,test01
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
class com.sun.proxy.$Proxy4
Opening JDBC Connection
Created connection 1039949752.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dfc5fb8]
==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ?
==> Parameters: 1(Integer)
<== Columns: id, lastName, email, gender
<== Row: 1, admin, jerry@qq.com, 女
<== Total: 1
class com.sun.proxy.$Proxy4
Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dfc5fb8]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dfc5fb8]
Returned connection 1039949752 to pool.
Process finished with exit code 0
三、全局配置文件
1.配置文件内容
全局配置文件中,各个标签要按照如下的顺序进行配置,因为Mybatis加载配置文件的原码中是按照这个顺序进行解析的。
<configuration>
<!-- 配置顺序如下
properties
settings
typeAliases
typeHandlers
objectFactory
plugins
environments
environment
transactionManager
dataSource
mappers
-->
</configuration>
各个标签说明如下:
-
properties(属性)
-
settings(全局配置参数)
-
typeAliases(类型别名)
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境子属性对象)
-
databaseIdProvider(数据库厂商标识)
-
mappers(映射器)
1.1properties(属性)
这些属性可以在外部进行配置,并可以进行动态替换。
在classpath下定义dbconfig.properties文件
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true
jdbc.driver=com.mysql.jdbc.Driver
全局配置文件中引用如下:
<properties resource="dbconfig.properties"/>
<!--
1.mybatis可以使用 properties 来引入外部properties 配置文件的内容
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
-->
<environments default="mysql">
<environment id="mysql">
<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>
如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
- 首先读取在 properties 元素体内指定的属性。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
- 最后读取作为==方法参数即(parameterType)==传递的属性,并覆盖之前读取过的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
1.2settings(全局配置参数)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods )。 | true | false | false (在 3.4.1 及之前的版本中默认为 true) |
multipleResultSetsEnabled | 是否允许单个语句返回多结果集(需要数据库驱动支持)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE : 不做任何反应WARNING : 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN )FAILING : 映射失败 (抛出 SqlSessionException ) | NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成使用的默认脚本语言。 | 一个类型别名或全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) | 一个类型别名或全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) | true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) | 一个类型别名或完全限定类名。 | 未设置 |
shrinkWhitespacesInSql | 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) | true | false | false |
defaultSqlProviderType | Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type (or value ) attribute on sql provider annotation(e.g. @SelectProvider ), when these attribute was omitted. | A type alias or fully qualified class name | Not set |
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
本例中使用的settings元素有:
<!--settings:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--设置驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1.3 typeAliases(类型别名)
类型别名可为 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>
当这样配置时,Blog
可以用在任何使用 domain.blog.Blog
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
所以,我们在别名的时候,不要占用上面已有的别名,它们都是大小写不敏感的。
1.4typeHandlers(类型处理器)
MyBatis 在设置预处理语句(PreparedStatement)中的参数,或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
理解:用于处理Java类型和Jdbc类型之间的转换,mybatis有许多内置的TypeHandler
<select id="getEmpById" parameterType="int" resultType="com.wangsuhang.mybatis_01_helloworld.pojo.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
1.5objectFactory(对象工厂)
mybatis会根据resultType
或resultMap
的属性来将查询得到的结果封装成对应的Java类,它有一个默认的DefaultObjectFactory,用于创建对象实例,这个标签用的也不多
1.6plugins(插件)
可以用来配置mybatis的插件,比如在开发中经常需要对查询结果进行分页,就需要用到pageHelper分页插件,这些插件就是通过这个标签进行配置的。
<!-- PageHelper 分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
1.7environments(环境配置)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
- 每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果忽略了环境参数,那么将会加载默认环境,如下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
environments 元素定义了如何配置环境。
environment:指定据具环境
- id:指定当前环境的唯一标识
- transactionManager:事务管理器
- dataSource:数据源
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
注意一些关键点:
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。
默认环境和环境 ID 顾名思义, 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
本例中使用如下:
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
1.8databaseIdProvider(数据库厂商标识)
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。 MyBatis 会加载带有匹配当前数据库 databaseId
属性和所有不带 databaseId
属性的语句。 如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql1"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
在mapper.xml中使用:
<select id="getEmpById" resultType="com.wangsuhang.mybatis_02_config.pojo.Employee"
databaseId="mysql1">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
Mybatis匹配规则如下:
- 如果没有配置databaseIdProvider标签,那么databaseId=null;
- 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上,则设置databaseId=配置指定的值,否则,依旧为null;
- Mybatis会加载不带databaseId属性和带有匹配当前数据库databaseId属性的所有语句。如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃。
1.9mappers(映射器)
mapper配置的几种方法:
-
<mapper resource=" " />
使用相对于类路径的资源引用
<mappers> <mapper resource="EmployeeMapper3.xml" /> <mapper resource="EmployeeMapperPlus.xml" /> <mapper resource="DepartmentMapper.xml"/> </mappers>
-
<mapper url=" " />
使用完全限定资源定位符(UEL)
<mapper url="file:///F:\code\MyBatis3\src\main\resources\EmployeeMapper2.xml" />
-
<mapper class=" " />
使用映射器接口实现类的完全限定类名
注意:
1.有sql映射文件,映射文件名必须和接口同名,并且与接口统一目录下;
2.没有sql映射 文件,所有的sql都是利用注解写在接口上
<mappers> <mapper resource="EmployeeMapper2.xml" /> <mapper class="com.wangsuhang.mybatis_02_config.mapper.EmployeeMapperAnnotation"/> </mappers>
本例中使用的是第二种方式:
package com.wangsuhang.mybatis_02_config.mapper;
import com.wangsuhang.mybatis_01_helloworld.pojo.Employee;
import org.apache.ibatis.annotations.Select;
/**
* @author suahng
* @date 2021-07-06 20:23
* @dec
*/
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id = #{id}")
public Employee getEmpById(Integer id);
}
-
<package name=" " />
将包内的映射器接口实现全部注册为映射器
注意:
此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
<mappers> <package name="org.mybatis.builder"/> </mappers>
四、参数传递
1.输入参数
1.1单个参数
可以接收基本类型,对象类型,集合类型的值。这种情况下Mybatis可以直接使用这个参数,不需要经过任何处理。
dao接口:
package com.wangsuhang.mybatis_03_mapper.mapper; import com.wangsuhang.mybatis_03_mapper.pojo.Employee; /** * @author suahng * @date 2021-07-06 14:18 * @dec */ public interface EmployeeMapper { public Employee getEmpById(Integer id); }
mapper.xml文件中对应的sql语句:
<select id="getEmpById" resultType="com.wangsuhang.mybatis_03_mapper.pojo.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
测试程序及结果
@Test
public void test() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1.获取到的SQLSession不会自动提交数据
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpById(1);
System.out.println(emp);
sqlSession.commit();
}finally {
sqlSession.close();
}
}
Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? ==> Parameters: 1(Integer) <== Columns: id, lastName, email, gender <== Row: 1, admin, jerry@qq.com, 女 <== Total: 1 Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool.
1.2.多个参数
1.2.1多个参数-按位置
任意多个参数,都会被Mybatis重新包装成一个Map传入。Map的key是param1,param2… 或arg0,arg1…;value就是参数的值;
dao接口:
List<Employee> getEmpByIdOrLastName(Integer id,String lastName);
mapper.xml文件中对应的sql语句:
<!-- Employee getEmpByIdOrLastName(Integer id,String lastName); --> <select id="getEmpByIdOrLastName" resultType="com.wangsuhang.mybatis_03_mapper.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where id = #{param1} or last_name = #{arg1} </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); List<Employee> emps = mapper.getEmpByIdOrLastName(1, "admin"); emps.forEach(System.out::println);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 36333492. Returned connection 36333492 to pool. Opening JDBC Connection Checked out connection 36333492 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@22a67b4] ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? or last_name = ? ==> Parameters: 1(Integer), admin(String) <== Columns: id, lastName, email, gender <== Row: 1, admin, jerry@qq.com, 女 <== Total: 1 Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@22a67b4] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@22a67b4] Returned connection 36333492 to pool.
1.2.2多个参数-使用@Param
为参数使用@param起一个名字,Mybatis就会将这些参数封装进map中,key就是我们自己指定的名字。
即在方法中形参前面加入@param(“自定义参数”),mapper.xml文件中使用#{自定义参数名}
dao接口:
public Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);
mapper.xml文件中对应的sql语句:
<select id="getEmpByIdAndLastName" resultType="com.wangsuhang.mybatis_03_mapper.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where id = #{id} and last_name = #{lastName} </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpByIdAndLastName(1, "admin"); System.out.println(emp);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? and last_name = ? ==> Parameters: 1(Integer), admin(String) <== Columns: id, lastName, email, gender <== Row: 1, admin, jerry@qq.com, 女 <== Total: 1 Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
1.2.3多个参数-使用对象(POJO)
当这些参数属于我们业务在中的POJO时,我们直接传递POJO。
使用java对象传递参数,java的属性值就是SQL语句需要的参数值。每个属性就是一个参数。
语法格式:#{property,javaType=java中数据类型名,jdbcType=数据库中数据类型名};javaType,jdbcType的类型Mybatis可以检测出来,一般不需要设置,常用格式为#{property};
dao接口:
public Employee getEmpbyPojo(Employee employee);
mapper.xml文件中对应的sql语句:
<select id="getEmpbyPojo" parameterType="com.wangsuhang.mybatis_03_mapper.pojo.Employee" resultType="com.wangsuhang.mybatis_03_mapper.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where id = #{id} or last_name = #{lastName} </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Employee employee = new Employee(); employee.setId(1); employee.setLastName("admin"); Employee emp = mapper.getEmpbyPojo(employee); System.out.println(emp);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? or last_name = ? ==> Parameters: 1(Integer), admin(String) <== Columns: id, lastName, email, gender <== Row: 1, admin, jerry@qq.com, 女 <== Total: 1 Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
异常测试:
将SQL语句中字段名输入错误后测试,将lastName改为lastname:
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'lastname' in 'class com.wangsuhang.mybatis_03_mapper.pojo.Employee' ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'lastname' in 'class com.wangsuhang.mybatis_03_mapper.pojo.Employee' Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'lastname' in 'class com.wangsuhang.mybatis_03_mapper.pojo.Employee' Process finished with exit code -1
1.2.4多个参数-使用map
也可将多个参数封装为map,直接传递,mapper.xml文件使用#{key}引用参数值。
dao接口:
public Employee getEmpByMap(Map<String,Object> map);
mapper.xml文件中对应的sql语句:
<select id="getEmpByMap" resultType="com.wangsuhang.mybatis_03_mapper.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where id = #{id} and last_name = #{lastName} </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); HashMap<String, Object> map = new HashMap<>(); map.put("id",1); map.put("lastName","admin"); Employee emp = mapper.getEmpByMap(map); System.out.println(emp);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? and last_name = ? ==> Parameters: 1(Integer), admin(String) <== Columns: id, lastName, email, gender <== Row: 1, admin, jerry@qq.com, 女 <== Total: 1 Employee(id=1, lastName=admin, email=jerry@qq.com, gender=女) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
2.输出参数
2.1使用resultType
resultType:执行SQL得到ResultSet转换的类型,使用类型的完全限定名或别名。注意,如果返回的是集合,那应该设置为集合包含的类型,而不是结合本身。
resultType和resultMap,不能同时使用。
2.1.1简单类型
简单类型:8种java原始类型+String
dao接口:
public int getEmpCount();
mapper.xml文件中对应的sql语句:
<select id="getEmpCount" resultType="int"> select count(*) from tbl_employee </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); int count = mapper.getEmpCount(); System.out.println(count);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select count(*) from tbl_employee ==> Parameters: <== Columns: count(*) <== Row: 30013 <== Total: 1 30013 Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
2.1.2对象类型(POJO)
前面几个例子,一直在用这个,就不再另外举例了。
2.1.3map
SQL的查询结果作为Map 中的key和value;
dao接口:
// 返回一条记录的map:key是列名,值就是对于的值 public Map<String,Object> getEmpByIdReturnMap(Integer id);
mapper.xml文件中对应的sql语句:
<select id="getEmpByIdReturnMap" resultType="java.util.Map"> select * from tbl_employee where id = #{id} </select>
测试程序及结果:
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Map<String, Object> emp = mapper.getEmpByIdReturnMap(1); System.out.println(emp);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: select * from tbl_employee where id = ? ==> Parameters: 1(Integer) <== Columns: id, last_name, email, gender, d_id <== Row: 1, admin, jerry@qq.com, 女, 1 <== Total: 1 {gender=女, d_id=1, last_name=admin, id=1, email=jerry@qq.com} Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
2.2使用resultMap
resultMap可以自定义SQL的结果和java对象属性的映射关系。更灵活的把列值赋值给指定的属性。
使用方式:
- 先定义resultMap,指定列名和属性的对应关系
- 在
<select>
中吧resultType替换成resultMap1.2.1准备工作
每个员工有与之对应的部门,建立部门表(上面已建立),和改造员工表(加一个字段d_id,表示该员工对应的部门id)
改后的Employee.java,–>EmployeePlus.java
package com.wangsuhang.mybatis_03_mapper.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author suahng * @date 2021-07-06 9:56 * @dec */ @Data @AllArgsConstructor @NoArgsConstructor public class EmployeePlus { private Integer id; private String lastName; private String email; private String gender; private Department dept; }
部门表对应的POJO,Department.java
package com.wangsuhang.mybatis_03_mapper.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * @author suahng * @date 2021-07-09 19:50 * @dec */ @Data @AllArgsConstructor @NoArgsConstructor public class Department { private Integer id; private String departmentName; //该部门下的所有员工信息 private List<EmployeePlus> emps; }
1.2.2一对一查询
场景:
查询Employee的同时查询员工对应的部门
Employee====>Department
一个员工有与之对应的部门信息dao接口:
public EmployeePlus getEmpAndDeptById(Integer id);
方式一:级联属性封装结果集
mapper.xml文件中对应的sql语句:
<!-- 方法1:级联属性封装结果集--> <resultMap id="MyDifEmp" type="com.wangsuhang.mybatis_03_mapper.pojo.EmployeePlus"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender" /> <result column="did" property="dept.id"/> <result column="dept_name" property="dept.departmentName"/> </resultMap> <!-- public Employee getEmpAndDeptById(Integer id); --> <select id="getEmpAndDeptById" resultMap="MyDifEmp2"> SELECT e.id id,e.last_name last_name,e.email email,e.gender gender,e.d_id d_id,d.id did,d.dept_name dept_name FROM tbl_employee e JOIN tbl_dept d ON e.d_id = d.id WHERE e.id = #{id} </select>
测试程序及结果:
EmployeeMapperPlus mapper = sqlSession.getMapper(EmployeeMapperPlus.class); EmployeePlus emp = mapper.getEmpAndDeptById(1); System.out.println(emp);
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: SELECT e.id id,e.last_name last_name,e.email email,e.gender gender,e.d_id d_id,d.id did,d.dept_name dept_name FROM tbl_employee e JOIN tbl_dept d ON e.d_id = d.id WHERE e.id = ? ==> Parameters: 1(Integer) <== Columns: id, last_name, email, gender, d_id, did, dept_name <== Row: 1, admin, jerry@qq.com, 女, 1, 1, 开发部 <== Total: 1 EmployeePlus(id=1, lastName=admin, email=jerry@qq.com, gender=女, dept=Department(id=1, departmentName=开发部, emps=null)) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
方法2:使用association定义关联单个对象的规则
mapper.xml文件中对应的sql语句:
<!-- 方法2:使用association定义关联单个对象的规则 --> <resultMap id="MyDifEmp2" type="com.wangsuhang.mybatis_03_mapper.pojo.EmployeePlus"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender" /> <!-- association可以指定联合的javaBean对象 property="dept":指定哪个属性是联合的对象 JavaType:指定哪个属性对象的类型 [不能省略] --> <association property="dept" javaType="com.wangsuhang.mybatis_03_mapper.pojo.Department"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> </association> </resultMap> <!-- public Employee getEmpAndDeptById(Integer id); --> <select id="getEmpAndDeptById" resultMap="MyDifEmp2"> SELECT e.id id,e.last_name last_name,e.email email,e.gender gender,e.d_id d_id,d.id did,d.dept_name dept_name FROM tbl_employee e JOIN tbl_dept d ON e.d_id = d.id WHERE e.id = #{id} </select>
测试程序及结果同上,就不在写了。
association:表示进行关联查询单条记录
property:表示关联查询的结果存储在com.wangsuhang.mybatis_03_mapper.pojo.EmployeePlus的dep属性中
javaType:表示关联查询的结果类型
<id property="did" column="id"/>
:查询结果的did列对应关联对象的id属性;
<result property="。departmentName" column="dept_name"/>
:查询结果的dept_name列对应关联对象的departmentName属性1.2.2多对一查询
场景二:
查询部门的时候将部门对应的所有员工信息也查询出来:详细信息在DepartmentMapper.xml中dao接口:
public Department getDeptByIdPlus(Integer id);
mapper.xml文件中对应的sql语句:
<!-- collection:嵌套结果集的方式,定义关联的集合类型元素的封装规则 --> <!-- public Department getDeptByIdPlus(Integer id); --> <resultMap id="MyDept" type="com.wangsuhang.mybatis_03_mapper.pojo.Department"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> <!-- collection:定义关联集合类型的封装规则 property:将要关联的信息映射到哪个属性中 ofType:指定集合里面元素的类型 --> <collection property="emps" ofType="com.wangsuhang.mybatis_03_mapper.pojo.EmployeePlus"> <!-- 定义这个集合中元素的封装规则 --> <id column="eid" property="id"/> <result column="last_name" property="lastName" /> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap> <select id="getDeptByIdPlus" resultMap="MyDept"> SELECT d.id did,d.dept_name dept_name, e.id eid,e.last_name last_name,e.email email,e.gender gender FROM tbl_dept d LEFT JOIN tbl_employee e ON d.id=e.d_id WHERE d.id=#{did} </select>
测试程序及结果:
DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class); Department dept = mapper.getDeptByIdPlus(1); System.out.println(dept);
Created connection 1333592072. Returned connection 1333592072 to pool. Opening JDBC Connection Checked out connection 1333592072 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] ==> Preparing: SELECT d.id did,d.dept_name dept_name, e.id eid,e.last_name last_name,e.email email,e.gender gender FROM tbl_dept d LEFT JOIN tbl_employee e ON d.id=e.d_id WHERE d.id=? ==> Parameters: 1(Integer) <== Columns: did, dept_name, eid, last_name, email, gender <== Row: 1, 开发部, 1, admin, jerry@qq.com, 女 <== Row: 1, 开发部, 4, wsh, wsh@qq.com, 男 <== Row: 1, 开发部, 7, smith, smith@qq.com, 男 <== Row: 1, 开发部, 8, allen, allen@qq.com, 男 <== Row: 1, 开发部, 11, smith, smith@qq.com, 男 <== Row: 1, 开发部, 12, allen, allen@qq.com, 男 <== Total: 6 Department(id=1, departmentName=开发部, emps=[EmployeePlus(id=1, lastName=admin, email=jerry@qq.com, gender=女, dept=null), EmployeePlus(id=4, lastName=wsh, email=wsh@qq.com, gender=男, dept=null), EmployeePlus(id=7, lastName=smith, email=smith@qq.com, gender=男, dept=null), EmployeePlus(id=8, lastName=allen, email=allen@qq.com, gender=男, dept=null), EmployeePlus(id=11, lastName=smith, email=smith@qq.com, gender=男, dept=null), EmployeePlus(id=12, lastName=allen, email=allen@qq.com, gender=男, dept=null)]) Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f7d0008] Returned connection 1333592072 to pool. Process finished with exit code 0
![在这里插入图片描述](https://img-blog.csdnimg.cn/b7bc8802456640919905d7c88da44be3.gif#pic_center)