1,myibatis延迟加载
存在多表关联查询时,不需要一下子把全部表都拿出来的时候,可以采用延迟加载,先取出单表,等需要时再取出其他表,这样可以大幅提高数据库性能。
相关table可自行建立,目录结构如下:
(1)延迟加载测试方法:
// 延迟加载测试
@Test
public void testfindOrdersUserLazyLoad(){
SqlSession session = MybatisUtil.getSqlSession();
OrdersMapper mapper = session.getMapper(OrdersMapper.class);
List<Orders> list = mapper.findOrdersUserLazyLoad();
for (Orders orders : list) {
System.out.println(orders.getUser());
}
}
(2)findOrdersUserLazyLoad相关的OrdersMapper.xml配置:
<!-- 延迟加载方式 查询订单信息并关联查询用户信息 -->
<select id="findOrdersUserLazyLoad" resultMap="LazyLoadFindOrdersUser">
SELECT * FROM orders
</select>
<!-- 延迟加载示例映射 -->
<resultMap type="com.mybatis.model.Orders" id="LazyLoadFindOrdersUser">
<!-- 定义订单信息映射 -->
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!--
select:指定需要延迟加载的statemet的id,如果不在当前的mapper.xml文件中,
则需要在前面加上对应的namespace
coLumn:指定关联查询的列 user_id
-->
<association property="user" javaType="com.mybatis.model.User"
select="com.mybatis.mapper.UserMapper.findById"
column="user_id" >
</association>
</resultMap>
延迟加载配置的核心就在于association里的配置,property表示要将查询的结果注入到那个属性中,由于它是个集合,需要javaType表示集合里元素的类型,coLumn:指定关联查询的列 。
(3)设置总的配置文件的延迟加载属性
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
(4)结果展示
List<Orders> list = mapper.findOrdersUserLazyLoad(); 这一步时仅会发出请求查询orders:
[DEBUG] - ==> Preparing: SELECT * FROM orders
[DEBUG] - ==> Parameters:
[DEBUG] - <== Total: 3
System.out.println(orders.getUser()); 到了这一步时,由于要调用user时,所以会去查找users,之后的循环没有发出sql:
[DEBUG] - ==> Preparing: select * from user where id = ?
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <== Total: 1
[DEBUG] - ==> Preparing: select * from user where id = ?
[DEBUG] - ==> Parameters: 10(Integer)
[DEBUG] - <== Total: 1
每根据一个user_id进行一次查询是否效率很低?
(5)aggressiveLazyLoading属性
如果在setting中加入 <setting name="aggressiveLazyLoading" value="false"/>
当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。
这句话有点绕,实际效果如下说明:
List<Orders> list = mapper.findOrdersUserLazyLoad(); 此时只执行一个sql:
[DEBUG] - ==> Preparing: SELECT * FROM orders
[DEBUG] - ==> Parameters:
[DEBUG] - <== Total: 3
for (Orders orders : list) { 到了这一步也没有发出sql
System.out.println(orders.getUser()); 在方法体里,每一次循环再发出一次sql(这种方式让延迟加载按需执行)
[DEBUG] - ==> Preparing: select * from user where id = ?
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <== Total: 1
但是测试的时候发现aggressiveLazyLoading设为true和false发出的sql是一样的,发出sql顺序相同,没发现区别,对懒加载也没影响。可能是同时存在两个懒属性时,调用其中一个,是否会加载其他懒属性的一个区分。
2,mybaits提供一级缓存,和二级缓存。
一级缓存SqlSession级别的缓存,同一数据可以保存在同一个
二级缓存是mapper级别,多个SqlSession去操作同一个Mapper的sql语句,不用重复发送sql请求。
一级缓存比较简单,同一条sql,查了一次就不会重复查,不用配置,当其他语句更新了数据之后,通过commit更新后再查即可即可。
不过测试的过程很有意思,如下代码:
@Test
public void testFindUser(){
SqlSession session = MybatisUtil.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// SqlSession session2 = MybatisUtil.getSqlSession();
// UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.findById(1);
// User user2 = mapper2.findById(1);
/*
* 执行了(更新,插入,删除),会自动清空sqlsession的一级缓存,
* 目的:为了避免脏读,保持缓存中数据是最新的
*/
User userrUser = new User();
userrUser.setId(1);
userrUser.setUsername("aaaaaaa");
mapper.updateUser(userrUser);
// 执行了更新,插入,删除操作后,需要执行 commit 提交
// session.commit();
User user1 = mapper.findById(1);
System.out.println(user);
System.out.println(user1);
session.close();
}
发现即使session没有commit,虽然只发出了一条sql,但是user1依然改变了username,很神奇。是不是说mybatis有一套机制避免脏读?
二级缓存同事在SqlMapConfig.xml和相关mapper.xml设置二级缓存即可。
SqlMapConfig.xml:
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
</settings>
UserMapper.xml:
<!-- 开启本mapper namespace下的二级缓存 -->
<cache/>
或者:<cache type="org.mybatis.caches.ehcache.EhcacheCache"/> (ehcache是一个分布式缓存框架)
测试方法:
@Test
public void testErJiFindUser(){
SqlSession session = MybatisUtil.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
SqlSession session2 = MybatisUtil.getSqlSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
SqlSession session3 = MybatisUtil.getSqlSession();
UserMapper mapper3 = session3.getMapper(UserMapper.class);
User user = mapper.findById(1);
System.out.println(user.getUsername());
// 执行关闭以后,才会将数据缓存到二级缓存中
session.close();
User user2 = mapper2.findById(1);
System.out.println(user2.getUsername());
session2.close();
/*
* 执行了(更新,插入,删除),会自动清空sqlsession的一级缓存,
* 目的:为了避免脏读,保持缓存中数据是最新的
*/
// User userrUser = new User();
// userrUser.setId(1);
// userrUser.setUsername("aaaaaaa");
// int i = mapper3.updateUser(userrUser);
// System.out.println(i);
// // 执行了更新,插入,删除操作后,需要执行 commit 提交
// session3.commit();
//
}
没开启二级缓存,则会发出三条select语句。
开启二级缓存后
[DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223f3642]
[DEBUG] - ==> Preparing: select * from user where id = ?
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <== Total: 1
王五
[DEBUG] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223f3642]
[DEBUG] - Closing JDBC Connection [
可以看出,这时只发出了一条sql。这里启用二级缓存的xml生成了一个HashMap,总的配置文件为何也要配置二级缓存?估计是为了整体管理
在statement中设置useCache=false可以禁用当前select语句的二级缓存:
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
为避免脏读,一个方法是commit,一个是加入flushCache="true":
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
3,mybatis的逆向工程
mybatis的逆向工程是通过如下jar包实现的
在项目的classpath配置generatorConfig.xml 如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test"
userId="root"
password="123456">
</jdbcConnection>
<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:aaaaaa"
userId="aaaaaa"
password="aaaaaa">
</jdbcConnection> -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.mybatis.model"
targetProject="d:\">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.mybatis.mapper"
targetProject="d:\">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.mybatis.mapper"
targetProject="d:\">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="items"></table>
<!-- <table schema="" tableName="sys_user"></table> -->
<!-- 有些表的字段需要指定java类型
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table> -->
</context>
</generatorConfiguration>
在测试类中输出如下:
package com.mybatis.test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorSqlmap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("d:/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
即可在d盘生成逆向工程项目