Mybatis上手教程–select语句延迟加载配置和使用案例
Contents
一延迟加载说明
前面,我们利用select语句的resultMap和association标签,完成了一个分步查询的场景。先查询员工信息,并获取员工所在部门id,然后根据这个部门id去查询员工所在部门信息。如果,使用log4j打印日志的时候,会看到数据库前后执行了2条SQL语句。
那么,如果我现在的场景是只需要查询员工信息,并不需要查询员工所在部门信息的话,是不是可以让它只执行查询员工信息的SQL语句,不去查部门表;等到我将来如果需要查询部门信息的时候,再去查部门表呢?
这在Mybatis里是可以实现的。通过设置lazyLoadingEnabled和aggresiveLazyLoading这2个属性值来实现。
二配置并使用延迟加载流程
0 配置log4j记录执行SQL并输出到console
首先,在pom.xml文件里,引入log4j,当然,这个步骤在最开始的上手教程里,已经完成了。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
然后,参考Mybatis官网「https://mybatis.org/mybatis-3/logging.html」,在resources路径下,新建名为log4j.properties的配置文件:
# Global logging configuration
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.com.knockatdatabase.EmployeeMapper=TRACE
log4j.logger.com.knockatdatabase.DepartmentMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志级别,先配置为DEBUG,然后针对com.knockatdatabase.EmployeeMapper和com.knockatdatabase.DepartmentMapper这2个接口实施日志记录。当然,我只是快速浏览一下官网,具体配置回头深入查看一下官网。
这样,在执行完测试的Java代码之后,在控制台上,可以看到下述记录的日志信息:
@Test
public void testGetEmployeeAndDepartmentByIdStep() {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employeeAndDepartmentById = mapper.getEmployeeAndDepartmentByIdStep(1);
System.out.println("employee info: " + employeeAndDepartmentById);
Department department = employeeAndDepartmentById.getDepartment();
System.out.println("department info:" + department);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
//控制台日志输出:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 825658265.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - ==> Preparing: select * from tbl_employee where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: select id,dept_name as deptName from tbl_dept where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
employee info: Employee{id=1, lastName='黄伟', gender='男', mail='huangwei@knockatdatabase.com', department=Department{id=1, deptName='市场部'}}
department info:Department{id=1, deptName='市场部'}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Returned connection 825658265 to pool.
至此,可以看到,这个测试代码,执行了2条SQL,并分别打印在了控制台上。
1配置mybatis-config.xml文件
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
这2个设置项,设置在settings标签下。lazyLoadingEnabled设置为true,表示全局开启懒加载模式;
aggresiveLazyLoading务必要设置为false。官方文档(官网下载压缩包,解压之后的PDF格式文档)是这么解释的,When enabled, any method call will load all the lazy properties of the object.
2改写测试Java代码
@Test
public void testGetEmployeeAndDepartmentByIdStep() {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employeeAndDepartmentById = mapper.getEmployeeAndDepartmentByIdStep(1);
System.out.println("employee info: " + employeeAndDepartmentById.getLastName());
// Department department = employeeAndDepartmentById.getDepartment();
// System.out.println("department info:" + department);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
//结果:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 825658265.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - ==> Preparing: select * from tbl_employee where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
employee info: 黄伟
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Returned connection 825658265 to pool.
此时,如果我们的Java代理里不需要取部门信息时,则可以看到SQL语句只执行了1条select * from tbl_employee where id=?,并没有执行查询部门信息的SQL语句。
反之,当我们的SQL语句,需要查询部门表的字段时,才会去执行查询部门表的SQL语句:
@Test
public void testGetEmployeeAndDepartmentByIdStep() {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employeeAndDepartmentById = mapper.getEmployeeAndDepartmentByIdStep(1);
System.out.println("employee info: " + employeeAndDepartmentById.getDepartment());
// Department department = employeeAndDepartmentById.getDepartment();
// System.out.println("department info:" + department);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
//结果:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 825658265.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - ==> Preparing: select * from tbl_employee where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: select id,dept_name as deptName from tbl_dept where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
employee info: Department{id=1, deptName='市场部'}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@31368b99]
DEBUG [main] - Returned connection 825658265 to pool.
三 小结
这个应该只能算是Mybatis的一个小功能吧,实际生产中真的会用到这种场景吗?暂且,先练手测试一把,记录一下即可。不过,也顺便快速上手了一下log4j的配置和使用。