文章目录
- 1.准备工作
- 1.1 建表
- 1.2 创建Maven项目
- 1.2.1 在pom文件中,修改打包方式为jar包,导入依赖
- 1.2.2 创建日志配置文件和jdbc.properties
- 1.2.3 创建Mybatis核心配置文件
- 1.2.4 创建实体类以及实体类之间的关系
- 1.2.5 创建SqlSession工具类
- 2.为什么要单独处理“多对一”和“一对多”关系
- 3.解决多对一映射关系的三种方案
- 3.1 方案1 级联属性赋值 【简单,但用的不多】
- 3.2 方案2 association
- 3.3 方案3 分步查询【用的比较多的处理方式】
- 3.3.1 查询员工信息
- 3.3.2 根据员工所对应的部门id查询部门信息
- 3.3.3 ⭕️ 延迟加载
- 3.3.3.1 注意
1.准备工作
1.1 建表
t_emp
t_dept
将两张表建立关系
添加测试数据:
1.2 创建Maven项目
1.2.1 在pom文件中,修改打包方式为jar包,导入依赖
pom.xml
<?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.rqs.mybatis</groupId>
<artifactId>MyBatis_demo2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<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>5.1.3</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
1.2.2 创建日志配置文件和jdbc.properties
** 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>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
1.2.3 创建Mybatis核心配置文件
** 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>
<!-- MyBatis核心配置文件,标签的顺序
properties?,settings?,typeAliases?,typeHandlers?,
objectFactory?,objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?-->
<properties resource="jdbc.properties"></properties>
<!-- 设置MyBatis的全局配置-->
<settings>
<!--将_自动映射为驼峰 , emp_name:empName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--设置类型别名,大小写不敏感。
如果不设置alias,则默认为类名(大小写不敏感)-->
<typeAliases>
<!--
typeAlias: 设置某个类型的别名
属性:
type 设置需要设置别名的类型
alias 设置某个类型的别名,如果不设置该属性,那么该类型拥有默认的类名,且不区分大小写
-->
<!--<typeAlias type="com.rqs.mybatis.pojo.User" alias="User"></typeAlias>-->
<!--推荐以包为单位,将包下所有的类型设置默认的类型别名且不区分大小写-->
<package name="com.rqs.mybatis.pojo"/>
</typeAliases>
<!--设置连接数据库的环境-->
<!--每一个environment都是具体连接数据库的环境-->
<!--
一个项目中只会用一个环境,default用于使用默认使用的环境:
id:表示连接数据库的环境的唯一标识 不能重复
-->
<environments default="development">
<!--
transactionmanager:设置事务管理方式
属性:
type="JDBC/MANAGED"
JDBC: 在当前环境中,执行sql时,使用的时jdbc原声的事务管理方式,需要手动的提交和回滚事务
MANAGED:被管理,例如Spring
-->
<environment id="development">
<transactionManager type="JDBC"/>
<!-- dataSource:配置数据源
属性"
type:设置数据源的类型
type=""
POOLED:表示使用数据库连接池缓存数据库连接
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<!--设置连接数据库的驱动-->
<property name="driver" value="${jdbc.driver}"/>
<!--设置连接地址-->
<property name="url" value="${jdbc.url}"/>
<!--注意:如果在建sql表单的时候选了字符集(如utf8),
这里的value要改成:value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8"-->
<!--用户名和密码-->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--
推荐以包为单位引入映射文件,要求:
1。 mapper接口所在的包要和映射文件所在的包一致
2。 mapper接口要和映射文件的名字一致-->
<!-- com.rqs.mybatis.mapper创建包时要用/分隔,这样才是目录,否则这整一个就只是文件夹名字而已-->
<package name="com.rqs.mybatis.mapper"/>
</mappers>
</configuration>
1.2.4 创建实体类以及实体类之间的关系
Dept类:
package com.rqs.mybatis.pojo;
public class Dept {
private Integer did;
private String deptName;
public Dept() {
}
public Dept(Integer did, String deptName) {
this.did = did;
this.deptName = deptName;
}
public Integer getDid() {
return did;
}
public void setDid(Integer did) {
this.did = did;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"did=" + did +
", deptName='" + deptName + '\'' +
'}';
}
}
Emp类:
package com.rqs.mybatis.pojo;
public class Emp {
private Integer eid;
private String empName;
private Integer age;
private String sex;
private String email;
private Dept dept;
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Emp() {
}
public Emp(Integer eid, String empName, Integer age, String sex, String email, Dept dept) {
this.eid = eid;
this.empName = empName;
this.age = age;
this.sex = sex;
this.email = email;
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"eid=" + eid +
", empName='" + empName + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", dept=" + dept +
'}';
}
}
设置实体类之间的关系
表和表之间是有关系的,实体类和实体类之间也有映射关系,所以需要设置实体类之间的关系,例如如何描述员工所对应的部门,如何描述部分所对应的员工。
1.多对一,需要在“多”所对应的实体类创建“一”对应的对象,即在Emp类中创建Dept对象
1.2.5 创建SqlSession工具类
SqlSessionUtils:
package com.rqs.mybatis.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.IOException;
import java.io.InputStream;
public class SqlSessionUtils {
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
2.为什么要单独处理“多对一”和“一对多”关系
假如需要查询员工以及员工所对应的部门信息,那么部门信息可以通过两表联查,在员工表中把员工的部门did查出来,再在部门表中把did对应的dept_name查出来,而这两个字段无法映射员工实体类中的Dept对象,并且数据库中这两个字段并不是实体类类型,所以只能通过resultMap来自定义映射关系。
3.解决多对一映射关系的三种方案
查询员工以及员工所对应的部门信息
3.1 方案1 级联属性赋值 【简单,但用的不多】
创建EmpMapper接口及其映射文件
EmpMapper接口
package com.rqs.mybatis.mapper;
import com.rqs.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmpMapper
/**
* 查询员工以及员工所对应的部门信息
*/
Emp getEmpAndDept(@Param("eid") Integer eid);
EmpMapper接口映射文件
<?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.rqs.mybatis.mapper.EmpMapper">
<resultMap id="empAndDeptResultMapOne" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<!-- Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMapOne">
<!--两表联查(用左外连接方式,还有内连接或右外连接)-->
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid=#{eid}
</select>
</mapper>
测试代码:
ResultMapTest:
package com.rqs.mybatis.test;
import com.rqs.mybatis.mapper.EmpMapper;
import com.rqs.mybatis.pojo.Emp;
import com.rqs.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class ResultMapTest {
@Test
public void testGetEmpAndDept() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp);
}
}
测试结果:
3.2 方案2 association
EmpMapper接口
package com.rqs.mybatis.mapper;
import com.rqs.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmpMapper
/**
* 查询员工以及员工所对应的部门信息
*/
Emp getEmpAndDept(@Param("eid") Integer eid);
EmpMapper接口映射文件
<?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.rqs.mybatis.mapper.EmpMapper">
<!--处理多对一映射关系 【方案2 association】-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
association:处理多对一的映射关系
property:需要处理多对一关系的属性名
javaType:该属性的类型
-->
<association property="dept" javaType="Dept">
<id property="did" column="did" ></id>
<result property="deptName" column="dept_name" ></result>
</association>
</resultMap>
<!-- Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
<!--两表联查(用左外连接方式,还有内连接或右外连接)-->
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid=#{eid}
</select>
</mapper>
测试代码:
ResultMapTest:
package com.rqs.mybatis.test;
import com.rqs.mybatis.mapper.EmpMapper;
import com.rqs.mybatis.pojo.Emp;
import com.rqs.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class ResultMapTest {
@Test
public void testGetEmpAndDept() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp);
}
}
测试结果:
3.3 方案3 分步查询【用的比较多的处理方式】
3.3.1 查询员工信息
EmpMapper接口
package com.rqs.mybatis.mapper;
import com.rqs.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmpMapper {
/**
* 通过分步查询查询员工以及员工所对应的部门信息
* 分步查询第一步:查询员工信息
*/
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
/**
* 通过分步查询查询部门以及部门中所有的员工信息
* 分步查询第二步:根据did查询员工信息
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);
}
EmpMapper接口映射文件
<?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.rqs.mybatis.mapper.EmpMapper">
<!--处理多对一映射关系 【方案2 association】-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
association:处理多对一的映射关系
property:需要处理多对一关系的属性名
javaType:该属性的类型
-->
<association property="dept" javaType="Dept">
<id property="did" column="did" ></id>
<result property="deptName" column="dept_name" ></result>
</association>
</resultMap>
<!-- Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
<!--两表联查(用左外连接方式,还有内连接或右外连接)-->
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid=#{eid}
</select>
</mapper>
3.3.2 根据员工所对应的部门id查询部门信息
DeptMapper接口文件
package com.rqs.mybatis.mapper;
import com.rqs.mybatis.pojo.Dept;
import org.apache.ibatis.annotations.Param;
public interface DeptMapper {
/**
* 分步查询的第二步:根据员工所对应的did查询部门信息
* @param did
* @return
*/
Dept getEmpAndDeptByStepTwo(@Param("did") int did);
}
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.rqs.mybatis.mapper.EmpMapper">
<!-- com.rqs.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo是这条sql语句的全类名-->
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
select中写什么?
由于需要将DeptMapper接口中的getEmpAndDeptByStepTwo()方法所查询出来的结果赋值给association中的property属性
而这个方法查询的结果是通过DeptMapper.xml中的sql语句查到的,所以需要知道这个sql语句的唯一标识
而sql的唯一标识是sql语句的命名空间namespace加上.SQLId,即com.rqs.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo
而命名空间namespace其实就是Mapper接口的全类名,SQLId其实就是Mapper接口中的方法名,所以也可以理解为 mapper接口的全类名.方法名
即 select属性中写写第二步的sql语句的namespace.SQLId或第二步的mapper接口的全类名.方法名
select: 设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:分步查询的条件 由前一个查询的结果提供
fetchType: 当开启了全局的延迟记载后,可通过此属性手动控制延迟加载的效果
fetchType:"lazy/eager" lazy表示延迟加载,eager表示立即加载
-->
<association property="dept"
select="com.rqs.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"">
</association>
</resultMap>
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
select * from t_emp where eid = #{eid}
</select>
</mapper>
DeptMapper.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.rqs.mybatis.mapper.DeptMapper">
<!-- Dept getEmpAndDeptByStepTwo(Integer did);-->
<!-- 分步查询可以实现懒加载-->
<!-- 在mybatis-config.xml中打开MyBatis的全局配置,将_自动映射为驼峰,这样就不用谢resultMap了,直接用resultType-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where did = #{did}
</select>
</mapper>
测试代码:
package com.rqs.mybatis.test;
import com.rqs.mybatis.mapper.EmpMapper;
import com.rqs.mybatis.pojo.Emp;
import com.rqs.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class ResultMapTest {
@Test
public void testGetEmpAndDeptByStep() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp);
}
}
测试结果:
3.3.3 ⭕️ 延迟加载
分步查询的优点:可以实现延迟加载
延迟加载默认不开启,必须在核心配置文件mybatis-config.xml中设置全局配置信息:
- lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
- aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个 属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加 载)|eager(立即加载)”
通过下面的例子来测试:
由于分步查询既能查询员工信息,又能根据分步查询的第二步来查询员工所对应的部门信息,所以在获取员工信息之后,顺便去查询了部门信息,只是没有显示。如下图所示
如果获取员工信息后不进行部门信息的查询,就需要在核心配置文件的全局设置中开启延时加载,如下图所示
此时在获取员工姓名的结果如下图所示,不会查询部门信息
如果不开启延时加载,查询员工姓名后再查询部门信息,sql语句会先全部执行,执行完之后,再去获取数据。
如果开启了延时加载,此时,sql不会全部执行。要获取员工姓名的时候,则执行获取员工姓名的sql语句
要部门信息的时候,则执行获取部门信息的sql语句,这就是延时加载。
3.3.3.1 注意
在核心配置文件mybatis-config.xml中设置了lazyLoadingEnabled和aggressiveLazyLoading的全局配置信息处理延时加载后,
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。另外还可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加 载)|eager(立即加载)”
此时可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”