0、准备工作
1、引入依赖
<dependencies>
<!-- 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>8.0.27</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2、创建数据表
CREATE TABLE t_dept(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20)
);
CREATE TABLE t_emp(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
age INT,
gender CHAR,
dept_id INT
);
INSERT INTO t_dept(dept_name) VALUES ('A'),('B'),('C');
INSERT INTO t_emp(emp_name,age,gender,dept_id) VALUES
('张三',20,'女',1),
('李四',22,'女',2),
('王五',21,'男',3),
('赵六',23,'男',1),
('田七',21,'女',3);
3、创建与数据表相对应 java 实体类
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
private Integer deptId;
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender, Integer deptId) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
this.deptId = deptId;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", deptId=" + deptId +
'}';
}
}
public class Dept {
private Integer deptId;
private String deptName;
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Dept() {
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
'}';
}
}
4、jdbc.properties 文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
5、MyBaits 核心配置文件
<?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>
<!--
MyBatis核心配置文件中,标签的顺序:
properties?,settings?,typeAliases?,typeHandlers?, objectFactory?,objectWrapperFactory?,
reflectorFactory?, plugins?,environments?,databaseIdProvider?,mappers?
-->
<!-- 引入 properties 文件 ,此后可以在当前文件中使用的方式访问value-->
<properties resource="jdbc.properties"/>
<typeAliases>
<package name="com.chenyixin.ssm.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>
<!--<mapper resource=""/>-->
<package name="com.chenyixin.ssm.mapper"/>
</mappers>
</configuration>
6、工具类:
package com.chenyin.ssm.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class SqlSessionUtil {
public static SqlSession getSqlSession() {
try {
// 获取核心的配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 创建 SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 通过核心配置文件多对应的字节输入流创建工厂类 SqlSessionFactory ,生产 SqlSession 对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
// 创建 SqlSession 对象(自动操作事务)
return sqlSessionFactory.openSession(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
7、log4j.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!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>
1、字段名和属性名不一致的情况
字段名和属性名不一致的情况,如何处理映射关系:
1、为查询的字段设置别名,和属性名一致
2、当字段符合 MySql 的要求使用,而属性符合 java 的要求使用驼峰,此时可以在 MyBatis 的核心文件中使用 setting 标签设置一个全局变量配置,可以自动将下划线映射为驼峰
如:emp_id -> empId
3、使用 resultMap 自定义映射处理
resultMap:设置自定义的映射关系
id:唯一表示
type:处理映射关系的实体类的类型
常用标签
id: 处理主键和实体类中属性的映射关系
result:处理普通字段和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql 查询出的某个字段
property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
1.1、使用全局配置
代码示例:
EmpMapper 接口:
public interface EmpMapper {
/**
* 根据 empId 获取 emp 信息
* @param empId
* @return
*/
Emp getEmpById(@Param("empId") Integer empId);
}
EmpMapper.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">
<!--
字段名和属性名不一致的情况,如何处理映射关系:
1、为查询的字段设置别名,和属性名一致
2、当字段符合 MySql 的要求使用,而属性符合 java 的要求使用驼峰,
此时可以在 MyBatis 的核心文件中使用 setting 标签设置一个全局变量配置,
可以自动将下划线映射为驼峰
如:emp_id -> empId
-->
<mapper namespace="com.chenyixin.ssm.mapper.EmpMapper">
<!--Emp getEmpById(@Param("empId") Integer empId);-->
<select id="getEmpById" resultType="emp">
<!--select emp_id empId,emp_name empName,age,gender,dept_id deptId from t_emp where emp_id = #{empId}-->
select * from t_emp where emp_id = #{empId}
</select>
</mapper>
配置核心文件:
<!-- 将下滑线映射为驼峰 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
测试类:
public class EmpMapperTest {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
@Test
public void getEmpById() {
Emp emp = empMapper.getEmpById(2);
System.out.println(emp);
// Emp{empId=2, empName='李四', age=22, gender='女', deptId=2}
}
}
1. 2、resultMap处理字段和属性的映射关系
EmpMapper 接口:
Emp getEmpById2(@Param("empId") Integer empId);
EmpMapper.xml 配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenyixin.ssm.mapper.EmpMapper">
<resultMap id="empMap" type="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<result column="dept_id" property="deptId"/>
</resultMap>
<!--Emp getEmpById(@Param("empId") Integer empId);-->
<select id="getEmpById" resultType="emp">
select * from t_emp where emp_id = #{empId}
</select>
<select id="getEmpById2" resultMap="empMap">
select * from t_emp where emp_id = #{empId}
</select>
</mapper>
测试:
@Test
public void getEmpById() {
Emp emp = empMapper.getEmpById2(2);
System.out.println(emp);
// Emp{empId=2, empName='李四', age=22, gender='女', deptId=2}
}
2、多对一映射处理
处理多对一的映射关系:
1、级联方式处理映射关系
2、使用association处理映射关系
3、分布查询方式处理映射关系
修改 emp 实体类:
package com.chenyixin.ssm.pojo;
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
private Dept dept;
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", dept=" + dept +
'}';
}
}
2.1、级联方式处理映射关系
EmpMapper 接口:
/**
* 根据 empId 获取 emp 的所有信息(包括该员工的部门信息)
* @param empId
* @return
*/
Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);
EmpMapper.xml 配置文件:
<resultMap id="getEmpAndDeptMap" type="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<result column="dept_id" property="dept.deptId"/>
<result column="dept_name" property="dept.deptName"/>
</resultMap>
<!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);-->
<!--处理方式1:级联方式处理多对一的映射关系-->
<select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptMap">
select *
from t_emp e
left join t_dept d
on e.dept_id = d.dept_id
where emp_id = #{empId}
</select>
测试:
@Test
public void getEmpAndDeptByEmpId() {
Emp emp = empMapper.getEmpAndDeptByEmpId(1);
System.out.println(emp);
// Emp{empId=1, empName='张三', age=20, gender='女', dept=Dept{deptId=1, deptName='A'}}
}
2.2、使用association处理映射关系
association:处理多对一的映射关系(处理实体类类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
注意:一对一的映射关系可以用多对一的方式去处理
EmpMapper 接口:
Emp getEmpAndDeptByEmpId2(@Param("empId") Integer empId);
EmpMapper.xml 配置文件:
<resultMap id="getEmpAndDeptMap2" type="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<association property="dept" javaType="Dept">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
<!--Emp getEmpAndDeptByEmpId2(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByEmpId2" resultMap="getEmpAndDeptMap2">
select *
from t_emp e
left join t_dept d
on e.dept_id = d.dept_id
where emp_id = #{empId}
</select>
测试:
@Test
public void getEmpAndDeptByEmpId2() {
Emp emp = empMapper.getEmpAndDeptByEmpId2(3);
System.out.println(emp);
// Emp{empId=3, empName='王五', age=21, gender='男', dept=Dept{deptId=3, deptName='C'}}
}
2.3、分步查询处理映射关系
association:处理多对一的映射关系(处理实体类类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
select : 设置分布查询的 sql 的唯一标识(namespace.id)
column:将查询出的某个字段作为分布查询的 sql 条件
EmpMapper 接口:
/**
* 分布查询:根据员工 id 查询对应员工信息 及其 该员工所对应的部门信息 (第二步)
* @param deptId
* @return
*/
Dept getEmpAndDeptByStopTwo(@Param("deptId") Integer deptId);
DeptMapper 接口:
/**
* 分布查询:根据员工 id 查询对应员工信息 及其 该员工所对应的部门信息 (第一步)
* @param empId
* @return
*/
Emp getEmpAndDeptByStopOne(@Param("empId") Integer empId);
DeptMapper.xml 配置文件:
<mapper namespace="com.chenyixin.ssm.mapper.DeptMapper">
<!--Dept getEmpAndDeptByStopTwo(@Param("deptId") Integer deptId);-->
<select id="getEmpAndDeptByStopTwo" resultType="dept">
select * from t_dept where dept_id = #{deptId}
</select>
</mapper>
EmpMapper.xml 配置文件:
<resultMap id="getEmpAndDeptStopMap" type="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<association property="dept" select="com.chenyixin.ssm.mapper.DeptMapper.getEmpAndDeptByStopTwo" column="dept_id"/>
</resultMap>
<!--Emp getEmpAndDeptByStopOne(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByStopOne" resultMap="getEmpAndDeptStopMap">
select * from t_emp where emp_id = #{empId}
</select>
测试:(记得开启全局配置)
@Test
public void getEmpAndDeptByStopOne() {
Emp emp = empMapper.getEmpAndDeptByStopOne(4);
System.out.println(emp);
}
结果:(查询了两次)
2.4、延迟加载
概念:对于实体类关联的属性到需要使用时才查询。也叫懒加载。
在全局变量中设置延迟加载:
<settings>
<!--将下滑线映射为驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载功能:需要配置两个配置项 -->
<!-- 1、将 lazyLoadingEnabled设置为 true,开启懒加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 2、将 aggressiveLazyLoading 设置为 false,关闭“积极的懒加载”,该属性默认为false,可以不用设置 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
测试1:(以在全局设置中开启了延迟加载)
@Test
public void getEmpAndDeptByStopOne() {
Emp emp = empMapper.getEmpAndDeptByStopOne(4);
System.out.println(emp.getEmpName());
}
结果:
因为全局变量会对所有的分布查询开启延迟加载,但也可以在 Mapper配置文件中的resultMap 设置延迟加载。
association:
fetchType:在开启了延迟加载的环境中,通过该属性设置当前分布查询是否使用延迟加载
fetchType=“eager(立即加载) | lazy(延迟加载)”
<resultMap id="getEmpAndDeptStopMap" type="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<association property="dept" select="com.chenyixin.ssm.mapper.DeptMapper.getEmpAndDeptByStopTwo"
column="dept_id" fetchType="eager"/>
</resultMap>
测试2:(测试代码与测试1相同)(以在全局设置中开启了延迟加载,但也在Mapper配置文件中的resultMap 设置了延迟加载)
结果:
3、一对多映射处理
处理一对多的映射关系
1、collection
2、分布查询
准备:
修改 dept 实体类:
public class Dept {
private Integer deptId;
private String deptName;
private List<Emp> emps;
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Dept() {
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
3.1、collection
collection (其子标签与 association 子标签用法相同)
collection:处理一对多的映射关系(处理集合类型的属性)
ofType:设置集合类型的属性中存储的数据的类型
DeptMapper 接口:
/**
* 根据部门 id 查询部门以及部门中的员工信息
* @param deptId
* @return
*/
Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
DeptMapper.xml 配置文件:
<resultMap id="getDeptAndEmpMap" type="Dept">
<id column="dept_id" property="deptId"/>
<id column="dept_name" property="deptName"/>
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId"/>
<id column="emp_name" property="empName"/>
<id column="age" property="age"/>
<id column="gender" property="gender"/>
</collection>
</resultMap>
<!--Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByDeptId" resultMap="getDeptAndEmpMap">
select *
from t_dept d
left join t_emp e on d.dept_id = e.dept_id
where d.dept_id = #{deptId}
</select>
测试:
public class DeptMapperTest {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
@Test
public void getDeptAndEmpByDeptId() {
Dept dept = deptMapper.getDeptAndEmpByDeptId(1);
System.out.println(dept);
// Dept{deptId=1, deptName='A',
// emps=[Emp{empId=1, empName='张三', age=20, gender='女', dept=null},
// Emp{empId=4, empName='赵六', age=23, gender='男', dept=null}]}
}
}
3.2、分步查询
DeptMapper 接口:
/**
* 通过分布查询 查询部门以及部门中的员工信息的第一步
* @param deptId
* @return
*/
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);
EmpMapper 接口:
/**
* 通过分布查询 查询部门以及部门中的员工信息的第二步
* @param deptId
* @return
*/
Emp getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);
EmpMapper.xml 配置文件:
<!--Emp getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByStepTwo" resultType="emp">
select * from t_emp where dept_id = #{deptId};
</select>
DeptMapper.xml 配置文件:
<resultMap id="getDeptAndEmpStepMap" type="Dept">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
<collection property="emps"
select="com.chenyixin.ssm.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"/>
</resultMap>
<!--Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpStepMap">
select * from t_dept where dept_id = #{deptId};
</select>
测试:
@Test
public void getDeptAndEmpByStepOne() {
Dept dept = deptMapper.getDeptAndEmpByStepOne(3);
System.out.println(dept);
}
结果:
4、总结
字段名和属性名不一致的情况,如何处理映射关系:
1、为查询的字段设置别名,和属性名一致2、当字段符合 MySql 的要求使用,而属性符合 java 的要求使用驼峰,
此时可以在 MyBatis 的核心文件中使用 setting 标签设置一个全局变量配置,
可以自动将下划线映射为驼峰
如:emp_id -> empId3、使用 resultMap 自定义映射处理
resultMap:设置自定义的映射关系
id:唯一表示
type:处理映射关系的实体类的类型常用标签
id: 处理主键和实体类中属性的映射关系
result:处理普通字段和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql 查询出的某个字段
property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名映射关系处理:
使用 resultMap 自定义映射处理
处理多对一的映射关系:
1、级联方式处理映射关系
2、使用 association 处理映射关系
3、分布查询方式处理映射关系
处理一对多的映射关系
1、collection
2、分布查询
resultMap:设置自定义的映射关系
id:唯一表示
type:处理映射关系的实体类的类型常用标签
id: 处理主键和实体类中属性的映射关系
result:处理普通字段和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql 查询出的某个字段
property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名association:处理多对一的映射关系(处理实体类类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
select : 设置分布查询的 sql 的唯一标识(namespace.id)
column:将查询出的某个字段作为分布查询的 sql 条件
fetchType:在开启了延迟加载的环境中,通过该属性设置当前分布查询是否使用延迟加载
fetchType=“eager(立即加载) | lazy(延迟加载)”注意:一对一的映射关系可以用多对一的方式去处理
collection:处理一对多的映射关系(处理集合类型的属性)
property:设置需要处理映射关系的属性的属性名
ofType:设置集合类型的属性中存储的数据的类型
select : 设置分布查询的 sql 的唯一标识(namespace.id)
column:将查询出的某个字段作为分布查询的 sql 条件
fetchType:在开启了延迟加载的环境中,通过该属性设置当前分布查询是否使用延迟加载
fetchType=“eager(立即加载) | lazy(延迟加载)”注意:多对多的映射关系可以用一对多的方式去处理
全局变量的设置:
<settings> <!--将下滑线映射为驼峰 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启延迟加载功能:需要配置两个配置项 --> <!-- 1、将 lazyLoadingEnabled设置为 true,开启懒加载功能 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 2、将 aggressiveLazyLoading 设置为 false,关闭“积极的懒加载”,该属性默认为false,可以不用设置 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>