Mybatis(3、延迟加载、查询缓存、与ehcache整合、逆向工程、与spring整合)

延迟加载

延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

使用association实现延迟加载

需要先定义连个mapper的方法对应的statements。

1、只查询订单信息

在查询订单的statement中使用association去延迟加载(执行)下边的statement(关联查询用户信息)

<!-- 查询订单关联用户,用户信息需要延迟加载 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
	SELECT * FROM orders
</select>

2、关联查询用户信息

      通过上边查询到的订单信息中user_id去关联查询用户信息

         使用UserMapper.xml中的findUserById
<select id="findUserById" parameterType="int" resultType="user">
		SELECT * FROM USER WHERE id=#{value}
</select>

上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候先去查询findUserBuyId,通过resultMap的定义将延迟加载执行配置起来。

延迟加载resultMap

使用association中的select指定延迟加载去执行的statement中的id

        <!-- 延迟加载的resultMap -->
	<resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserLazyLoadingResultMap">
			<!--对订单信息进行映射配置  -->
			<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:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
			要使用userMapper.xml中findUserById完成根据用户id(user_id)用户信息的查询,
			如果findUserById不在本mapper中需要前边加namespace
			column:订单信息中关联用户信息查询的列,是user_id
			关联查询的sql理解为:
			SELECT
				orders.*, (
					SELECT
						username
					FROM
						USER
					WHERE
						orders.user_id = USER .id
				) username,
				(
					SELECT
						sex
					FROM
						USER
					WHERE
						orders.user_id = USER .id
				) sex
			FROM
				orders
			 -->
			<association property="user"  javaType="cn.itcast.mybatis.po.User"
			 select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
			<!-- 实现对用户信息进行延迟加载 -->
			</association>
	</resultMap>

Mapper.java

// 查询订单关联查询用户,用户信息是延迟加载
public List<Orders> findOrdersUserLazyLoading() throws Exception;

测试

1、执行上边mapper方法(findOrdersUserLazyLoading),内部去调用cn.itcast.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查询orders信息(单表)。

2、在程序中去遍历上一步骤查询出的List<Orders>,当我们调用Orders中的getUser方法时,开始进行延迟加载。

3、延迟加载,去调用UserMapper.xml中findUserbyId这个方法获取用户信息。

延迟加载的配置

mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。

在mybatis核心配置文件中配置:

lazyLoadingEnabled、aggressiveLazyLoading


mybatis.xml的配置

<settings>
		<!-- 打开延迟加载 的开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 将积极加载改为消极加载即按需要加载 -->
		<setting name="aggressiveLazyLoading" value="false"/>
</settings>



查询缓存

一级缓存

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

mybaits提供一级缓存,和二级缓存。


一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 

为什么要用缓存?

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
工作原理:

一级缓存的测试
@Test
	public void testCache1() throws Exception{
		SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);		
		//下边查询使用一个SqlSession
		//第一次发起请求,查询id为1的用户
		User user1 = userMapper.findUserById(1);
		System.out.println(user1);		
//		如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。		
		//更新user1的信息
		user1.setUsername("测试用户22");
		userMapper.updateUser(user1);
		//执行commit操作去清空缓存
		sqlSession.commit();		
		//第二次发起请求,查询id为1的用户
		User user2 = userMapper.findUserById(1);
		System.out.println(user2);
		
		sqlSession.close();
		
	}

应用:

正式开发,是将mybatis和spring进行整合开发,事务控制在service中。

一个service方法中包括很多mapper方法调用。

service{

      //开始执行时,开启事务,创建SqlSession对象

      //第一次调用mapper的方法findUserById(1)

      //第二次调用mapper的方法findUserById(1),从一级缓存中取数据

      //方法结束,sqlSession关闭

}

如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。


二级缓存

原理:

首先开启mybatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

1、在核心配置文件SqlMapConfig.xml中加入

                <!-- 开启二级缓存 -->
		<setting name="cacheEnabled" value="true"/>
	</settings>

2、在UserMapper.xml中开启二级缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(hashMap)

<mapper namespace="test">
     <!—开启本namespace的二级缓存-->
     <cache/>

调用pojo实现序列化接口

public class User implements Serializable {	
	//属性名和数据库表的字段对应
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址

为了将缓存数据提取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样的内存。

测试方法

// 二级缓存测试
	@Test
	public void testCache2() throws Exception {
		SqlSession sqlSession1 = sqlSessionFactory.openSession();
		SqlSession sqlSession2 = sqlSessionFactory.openSession();
		SqlSession sqlSession3 = sqlSessionFactory.openSession();
		// 创建代理对象
		UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
		// 第一次发起请求,查询id为1的用户
		User user1 = userMapper1.findUserById(1);
		System.out.println(user1);		
		//这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
		sqlSession1.close();		
		//使用sqlSession3执行commit()操作
		UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
		User user  = userMapper3.findUserById(1);
		user.setUsername("张明明");
		userMapper3.updateUser(user);
		//执行提交,清空UserMapper下边的二级缓存
		sqlSession3.commit();
		sqlSession3.close();
		UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
		// 第二次发起请求,查询id为1的用户
		User user2 = userMapper2.findUserById(1);
		System.out.println(user2);
		sqlSession2.close();
	}

useCache配置

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

刷新缓存

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

 设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

mybatis整合ehcache

依赖jar
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

ehcache:是一个分布式的缓存框架

目的:为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)。

分布式方法

mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。

mybatis默认实现cache类


整合ehcache

配置mapper中cache中的type为ehcache对cache接口的实现类。
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 开启mapper的namespace下的二级缓存
type:指定cache接口的实现类的类型,mybatis默认使用PerpetualCache要和ehcache整合,
需要配置type为ehcache实现cache接口类型 
-->
 <cache type="cn.itcast.mybatis.util.PerpetualCache"/>

加入ehcache配置文件

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<diskStore path="F:\develop\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

spring和mybatis整合

在applicationContext.xml配置sqlSessionFactory和数据源。

<!-- 加载配置文件 -->
	<context:property-placeholder location="classpath:db.properties" />
	<!-- 数据源,使用dbcp -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxActive" value="10" />
		<property name="maxIdle" value="5" />
	</bean>
	<!-- sqlSessinFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 加载mybatis的配置文件 -->
		<property name="configLocation" value="mybatis/SqlMapConfig.xml" />
		<!-- 数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>

原始dao开发

dao接口

public interface UserDao {
	// 根据id查询用户信息
	public User findUserById(int id) throws Exception;
}

实现类

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
	@Override
	public User findUserById(int id) throws Exception {
		//继承SqlSessionDaoSupport,通过this.getSqlSession()得到sqlSessoin
		SqlSession sqlSession = this.getSqlSession();
		User user = sqlSession.selectOne("test.findUserById", id);
		return user;
	}
}

dao配置

<!-- 原始dao接口 -->	
	<bean id="userDao" class="cn.itcast.ssm.dao.UserDaoImpl">
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean>

测试代码

public class UserDaoImplTest {
	private ApplicationContext applicationContext;
	//在setUp这个方法得到spring容器
	@Before
	public void setUp() throws Exception {
		applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
	}
	@Test
	public void testFindUserById() throws Exception {
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		//调用userDao的方法
		User user = userDao.findUserById(1);
		System.out.println(user);
	}
}

mapper代理开发

mapper.xml和mapper.java

通过MapperScannerConfigurer进行扫描(建议使用)

<!-- mapper批量扫描,从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册 
	遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录 中
	自动扫描出来的mapper的bean的id为mapper类名(首字母小写)
	-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定扫描的包名 
		如果扫描多个包,每个包中间使用半角逗号分隔
		-->
		<property name="basePackage" value="cn.itcast.ssm.mapper"/>
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>		
	</bean>	

测试代码

@Test
	public void testFindUserById() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"classpath:spring/applicationContext.xml");
		UserMapper userMapper = (UserMapper) applicationContext
				.getBean("userMapper");
		User user = userMapper.findUserById(1);
		System.out.println(user);
	}

逆向工程

下载依赖
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

配置文件:
修改generatorConfig.xml的配置信息
主要修改的内容有:
1、 jdbcConnection 的数据库连接信息。
2、sqlMapGeneratortargetPackagemapper.xml映射文件生成存放的位置
3、table将需要生成的表的表名都配置到配置文件中
<?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/mybatis" userId="root"
			password="mysql">
		</jdbcConnection>
		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 
			和 NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="cn.itcast.ssm.po"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
		<!-- targetProject:mapper映射文件生成的位置 **需要配置**-->
		<sqlMapGenerator targetPackage="cn.itcast.ssm.mapper"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀-->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口生成的位置**需要配置** -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="cn.itcast.ssm.mapper" targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表**需要配置**-->
		<table tableName="items"></table>
	</context>
</generatorConfiguration>

执行生成的程序

@Test
	public void aa() throws Exception {
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("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);
	}

生成后的代码

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值