二、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,false | false |
aggressiveLazyLoading | 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载 | true ,false | true |
<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,false | true |
在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/8802683ehcache实现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/88028974.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(); } } }
相关文章推荐
- mysql实现master-slave的replication方案
- 图像傅里叶变换
- weblayer组件介绍
- Extension
- android Unity3D 游戏修改基础篇
- MSSQL 递归CTE的应用通过子级获取所有对应的父级及其本身(二)
- UITableView 的方法总汇
- php分10个不同等级压缩优化图片
- Remove Nth Node From End of List
- Linux常用命令
- makefile 测试
- HDU 1325 Is It A Tree?
- JAVA 正则表达式 (超详细)
- SAT作文之评分标准
- Javascript关于JSON集合的几种循环方法
- phprpc 使用实例
- 浏览器检测
- org.tinygroup.service-服务
- Android中使用代码截图的各种方法总结
- hadoop编程技巧(3)---定义自己的区划类别Partitioner