您的位置:首页 > 其它

二、mybatis进阶

2015-06-09 16:49 337 查看

1. 关联查询

1.1 多对一

PO类

public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;

//用户信息
private User user;
。。。
}


映射文件

<resultMap type="cn.com.model.Orders" id="userordermap">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />

<association property="user" javaType="cn.com.model.User">
<id property="id" column="user_id" />
<result property="username" column="username" />
<result property="address" column="address" />
</association>
</resultMap>

<select id="findOrdersListResultMap" resultMap="userordermap">
SELECT  orders.*,   user.username,  user.address
FROM    orders, user
WHERE orders.user_id = user.id
</select>


多对一中,使用association 映射一方

<association property="user" javaType="cn.com.model.User">
<id property="id" column="user_id" />
<result property="username" column="username" />
<result property="address" column="address" />
</association>


“id” 会使用orders中的user_id 对id进行赋值

property : 多方中对应的一方的属性名

javaType:类型

1.2 一对多

PO类

public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;

//用户信息
private User user;

//订单明细
private List<Orderdetail> orderdetails;
。。。。
}


映射文件

<resultMap type="cn.com.model.Orders" id="userorderdetailmap">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
<association property="user" javaType="cn.com.model.User"> <id property="id" column="user_id" /> <result property="username" column="username" /> <result property="address" column="address" /> </association>
<collection property="orderdetails" ofType="cn.com.model.Orderdetail"> <id property="id" column="orderdetail_id" /> <result property="itemsId" column="items_id" /> <result property="itemsNum" column="items_num" /> </collection>
</resultMap>

<select id="findOrdersDetailList" resultMap="userorderdetailmap">
SELECT orders.*,user.username,user.address,
orderdetail.id orderdetail_id,orderdetail.items_id, orderdetail.items_num
FROM orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
</select>


一对多中,一方中会定义多方的一个集合,在映射文件中使用collection映射。

<collection property="orderdetails" ofType="cn.com.model.Orderdetail">
<id property="id" column="orderdetail_id" />
<result property="itemsId" column="items_id" />
<result property="itemsNum" column="items_num" />
</collection>


property:一方中的多方集合属性名称。

ofType:指定关联查询的结果集中的对象类型即List中的对象类型。

1.3 resultMap继承

上面的resultMap userorderdetailmap 可以继承userordermap ,复用重复部分

<resultMap type="cn.com.model.Orders" id="userorderdetailmap" extends="userordermap">

<collection property="orderdetails" ofType="cn.com.model.Orderdetail"> <id property="id" column="orderdetail_id" /> <result property="itemsId" column="items_id" /> <result property="itemsNum" column="items_num" /> </collection>
</resultMap>


resultMap 中用 extends=”父resultMap” 可复用已有的resultMap中的映射配置

1.4 多对多

需要关联查询映射的信息是:用户、订单、订单明细、商品信息

订单:一个用户对应多个订单,使用collection映射到用户对象的订单列表属性中

订单明细:一个订单对应多个明细,使用collection映射到订单对象中的明细属性中

商品信息:一个订单明细对应一个商品,使用association映射到订单明细对象的商品属性中

PO类

public class User {
private int id;
private String username;
private String sex;
private Date birthday;
private String address;

//一个用户对应多个订单
private List<Orders> orders = new ArrayList<Orders>();
}


public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;

//用户信息
private User user;

//订单明细
private List<Orderdetail> orderdetails;
。。
}


public class Orderdetail {

private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;

private Items items;
。。。。
}


映射文件

<resultMap type="cn.com.model.User" id="userOrderListResultMap">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="orders" ofType="cn.com.model.Orders">
<id column="id" property="id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.com.model.User"> <id property="id" column="user_id" /> <result property="username" column="username" /> <result property="address" column="address" /> </association>
<collection property="orderdetails" ofType="cn.com.model.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result property="ordersId" column="id"/>
<result property="itemsId" column="items_id"/>
<result property="itemsNum" column="items_num"/>
<association property="items" javaType="cn.com.model.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
<select id="findAll" resultMap="userOrderListResultMap">
SELECT orders.*, USER .username, USER .address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
items.name items_name,
items.detail items_detail
FROM orders, USER,orderdetail,items
WHERE orders.user_id = USER .id
AND orders.id = orderdetail.orders_id
AND orderdetail.items_id = items.id
</select>


2. 延迟加载

需要查询关联信息时,使用mybatis延迟加载特性可有效的减少数据库压力,首次查询只查询主要信息,关联信息(如订单用户关系,可先查询订单,需要用户信息)获取时再加载。

2.1 打开延迟加载开关

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

lazyLoadingEnabled、aggressiveLazyLoading

设置项描述允许值默认值
lazyLoadingEnabled全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载true,falsefalse
aggressiveLazyLoading当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载true ,falsetrue
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>


2.2 多对一延迟加载

PO类

一个用户可以有多个订单,多个订单归属一个用户

订单与用户是多对一关系,在订单实体类中增加User的属性

public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//用户信息
private User user;
。。。。。
}


映射文件

Orders映射文件

在Orders的映射文件中,通过association 配置关联属性,此处不在配置User的各个属性映射

增加select 和 colum属性配置

select=”cn.com.model.UserDao.findUserById”

在association 中配置select属性,指定懒加载关联查询使用的statement,findUserById在user.xml中,所以此处加上命名空间

column=”user_id”

在association 中配置column属性,指定懒加载关联查询使用的statement中需要传入的值

<?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="cn.com.dao.OrdersDao">
<resultMap type="cn.com.model.Orders" id="orderLazyUser">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />

<association property="user" javaType="cn.com.model.User"
select="cn.com.model.UserDao.findUserById" column="user_id">

</association>
</resultMap>
<select id="findOrdersLazyLoadUser" resultMap="orderLazyUser">
SELECT id,user_id,number,createtime,note  FROM  orders
</select>

</mapper>


User映射文件

此处 的findUserById 在orders映射文件中使用到

<mapper namespace="cn.com.model.UserDao">
<select id="findUserById" parameterType="int" resultType="cn.com.model.User">
SELECT * FROM USER WHERE id=#{value}
</select>
</mapper>


测试类

@Test
public void findOrdersLazyLoadUser() throws Exception{
String resource = "sqlmapconfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersDao ordersDao = sqlSession.getMapper(OrdersDao.class);
List<Orders> list = ordersDao.findOrdersLazyLoadUser();
for(Orders orders:list){
System.out.println(orders.getUser().getUsername());
}
sqlSession.close();
}


此处在System.out.println(orders.getUser().getUsername());才会查询数据库,加载User的值

3. 查询缓存

Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。

Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。

3.1 一级缓存

一级缓存区域是根据SqlSession为单位划分的。

同一个SqlSession每次查询会先从缓存区域找,如果找不到再从数据库查询,查询到数据将数据写入缓存。下一次再次执行相同的查询时,则会从缓存中取出,不再查询数据库。

Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

一级缓存是SQLSession级别的缓存。默认是开启的。

- 测试一级缓存

SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//第二次查询,由于是同一个session则不再向数据发出语句直接从缓存取出
User user2 = userMapper.findUserById(1);
System.out.println(user2);
//关闭session
session.close();


SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//在同一个session执行更新
user1 .setUsername("xxx");
userMapper.updateUser(user1 );
session.commit();
//第二次查询,虽然是同一个session但是由于执行了更新操作session的缓存被清空,这里重新发出sql操作
User user2 = userMapper.findUserById(1);
System.out.println(user2);


3.2 二级缓存

二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域。

每一个namespace会有一个自己的二级缓存,不同的 namespace用不同的二级缓存。

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

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

每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存

在SqlMapConfig.xml设置二级缓存的总开关默认是开启的,但是建议还是配置开启。

3.2.1 开启二级缓存

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

<setting name="cacheEnabled" value="true"/>


配置项描述允许值默认值
cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置true,falsetrue
mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

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

<mapper namespace="cn.com.dao.OrdersDao">
<!-- 开启本 mapper的 namespace下的二级缓存-->
<cache type=""/>


type指定实现mybatis cache 接口的实现类 类名,默认是org.apache.ibatis.cache.impl.PerpetualCache



3.2.2 pojo类实现序列化接口

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

public class Orders implements Serializable{
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//用户信息
private User user;
。。
}


3.2.3 测试1

String resource = "sqlmapconfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
OrdersDao ordersDao1 = sqlSession1.getMapper(OrdersDao.class);
//第一次查询,缓存中没数据,请求数据库查询
Orders  orders1 = ordersDao1.findById(3);
//sqlsession关闭
sqlSession1.close();

SqlSession sqlSession2 = sqlSessionFactory.openSession();
OrdersDao ordersDao2 = sqlSession2.getMapper(OrdersDao.class);
//第二次查询,缓存中有数据,从缓存中取出数据,不再请求数据库查询
Orders  orders2 = ordersDao2.findById(3);
sqlSession2.close();


3.2.4 测试2

String resource = "sqlmapconfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
OrdersDao ordersDao1 = sqlSession1.getMapper(OrdersDao.class);
//第一次查询,缓存中没数据,请求数据库查询
Orders  orders1 = ordersDao1.findById(3);
//sqlsession关闭
sqlSession1.close();

SqlSession sqlSession3 = sqlSessionFactory.openSession();
OrdersDao ordersDao3 = sqlSession3.getMapper(OrdersDao.class);
//第二次查询,缓存中有数据,从缓存中取出数据,不再请求数据库查询
Orders  orders3 = ordersDao3.findById(3);
orders3.setCreatetime(new Date());
ordersDao3.updateOrder(orders3);
sqlSession3.commit();
//执行提交,清空OrderMapper下边的二级缓存,防止脏读
sqlSession3.close();

SqlSession sqlSession2 = sqlSessionFactory.openSession();
OrdersDao ordersDao2 = sqlSession2.getMapper(OrdersDao.class);
//第三次查询,缓存中没有数据,请求数据库查询
Orders  orders2 = ordersDao2.findById(3);
sqlSession2.close();


3.2.5 特定查询禁用二级缓存(useCache配置)

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

<select id="findById" useCache="false" parameterType="int" resultMap="orderLazyUser">


3.2.6 刷新缓存(清空缓存)

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

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

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

3.3 mybatis整合ehcache

ehcache是一个分布式缓存框架。我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

3.3.1 整合原理

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



package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}


mybatis默认实现cache类是:

package org.apache.ibatis.cache.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {

private String id;

private Map<Object, Object> cache = new HashMap<Object, Object>();

public PerpetualCache(String id) {
this.id = id;
}

public String getId() {
return id;
}

public int getSize() {
return cache.size();
}

public void putObject(Object key, Object value) {
cache.put(key, value);
}

public Object getObject(Object key) {
return cache.get(key);
}

public Object removeObject(Object key) {
return cache.remove(key);
}

public void clear() {
cache.clear();
}

public ReadWriteLock getReadWriteLock() {
return null;
}

public boolean equals(Object o) {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
if (this == o) return true;
if (!(o instanceof Cache)) return false;

Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}

public int hashCode() {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
return getId().hashCode();
}
}


3.3.2 加入ehcache包

下载地址:http://download.csdn.net/detail/hxskmx/8802683



ehcache实现mybatis cache接口 实现类



3.3.3 mybatis整合ehcache

配置mapper中cache中的type为ehcache对cache接口的实现类型。

<mapper namespace="cn.congxing.model.User">
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>


3.3.4 加入ehcache的配置文件

在classpath下配置ehcache.xml

<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>


4. generator代码生成

4.1 下载generator

http://download.csdn.net/detail/hxskmx/8802897

4.2 配置文件

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/mybatis" userId="root"   password="123">
</jdbcConnection>

<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl" userId="demo" password="demo">
</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.congxing.model"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="cn.congxing.model"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="cn.congxing.dao"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="items"></table>
<table tableName="orders"></table>
<table tableName="orderdetail"></table>
<table tableName="user"></table>
<!-- <table schema="" tableName="sys_user"></table>
<table schema="" tableName="sys_role"></table>
<table schema="" tableName="sys_permission"></table>
<table schema="" tableName="sys_user_role"></table>
<table schema="" tableName="sys_role_permission"></table> -->

<!-- 有些表的字段需要指定java类型
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table> -->
</context>
</generatorConfiguration>


4.3 生成代码

package cn.congxing.test;

import java.io.File;
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.internal.DefaultShellCallback;

public class GeneratorSqlmap {

public void generator() throws Exception{

List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("config/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();
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: