mybatis框架--學習筆記(下)
8、高階對映:
(1)一對一查詢:
①使用resultType:
<!-- 一對一查詢:resultType -->
<select id="findOrdersUser" resultType="com.zwp.po.OrdersCustom">
select orders.*,
user.username,
user.sex,
user.address
from orders,user
where orders.user_id=user.id
</select>
//Orders的擴充套件類
public class OrdersCustom extends Orders{
//新增用屬性
private String username;
private String sex;
private String address;
//下面省略get和set方法..
}
②使用resultMap:(association)
使用resultMap將查詢結果中的訂單資訊對映到Orders物件中,在orders類中新增User屬性,將關聯查詢出來的使用者資訊對映到orders物件中的user屬性中。
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private User user;//在Orders中新增User屬性,一對一
//下面省略get和set方法
}
<!-- 一對一查詢:resultMap
使用resultMap將查詢結果中的訂單資訊對映到Orders物件中,在orders類中新增User屬性,
將關聯查詢出來的使用者資訊新增到orders物件的user屬性中-->
<resultMap type="com.zwp.po.Orders" id="OrdersUserResultMap">
<!-- 配置訂單資訊的對映 -->
<!-- id:指定查詢列中的唯一標識,即訂單資訊的唯一標識,如果有多個列組成唯一標識,則配置多個id
column:查詢出來的列名; property:對映到Orders的哪個屬性
-->
<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" />
<!-- 配置對映的關聯的使用者資訊 -->
<!-- association:用於對映關聯查詢的單個物件資訊
property:要將關聯查詢的使用者資訊對映到Orders中的哪個屬性 -->
<association property="user" javaType="com.zwp.po.User">
<!-- id:關聯查詢的唯一標識,column:指定唯一標識的使用者的列 -->
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="sex" property="sex" />
<result column="address" property="address" />
</association>
</resultMap>
<select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap">
select orders.*,
user.username,
user.sex,
user.address
from orders,user
where orders.user_id=user.id
</select>
小結:實現一對一查詢:
resultyType:使用resultType實現較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成對映。如果沒有查詢結果的特殊要求,建議使用resultyType。
resultMap:需要 單獨定義resultMap,實現有點麻煩,如果有對查詢結果有特殊要求,使用resultMap可以完成將關聯查詢對映到pojo的屬性中。
resultMap可以實現延時載入,resultyType無法實現延時載入。
(2)一對多查詢:(collection)
要求:對orders對映不能出現重複記錄。
思路:在order.java類中新增List<OrderDetail>orderDetails屬性,最終會將訂單資訊對映到orders中,訂單所對應的訂單明細對映到orders中的orderDetails屬性中。
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private User user;//使用者資訊,一對一
//訂單明細:一對多
private List<Orderdetail> orderdetail;
//省略get和set方法
}
<!-- 一對多查詢: -->
<resultMap type="com.zwp.po.Orders" id="OrdersAndOrderdetailResultMap" extends="OrdersUserResultMap">
<!-- 訂單資訊 -->
<!-- 使用者資訊 -->
<!-- extends繼承:可以不用再配置訂單資訊和使用者資訊的對映 -->
<!-- 訂單明細資訊:
一個訂單關聯查詢出了多條明細,要使用collection進行對映
collection:對關聯查詢多條對映到集合物件中;
property:將關聯查詢多條對映到pojo的哪個屬性中;
ofType:指定對映到list集合屬性中pojo的型別。
-->
<collection property="orderdetail" ofType="com.zwp.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
</collection>
</resultMap>
<select id="findOrdersAndOrderdetailResultMap" resultMap="OrdersAndOrderdetailResultMap">
select orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
from orders,user,orderdetail
where orders.user_id=user.id and orderdetail.orders_id=orders.id
</select>
小結:mybatis使用resultMap的collection對關聯查詢的多條記錄對映到一個list集合屬性中。
如果使用resultType實現:將訂單明細對映到orders中的ordertails中,需要自己處理,使用雙重迴圈遍歷,去掉重複記錄,將訂單明細放在ordertails中。
(3)多對多查詢:
對映思路:將使用者資訊對映到user中。
在user類中新增訂單列表屬性List<Orders>orderslist,將使用者建立的訂單對映到orderlist;
在Orders中新增訂單明細列表屬性List<OrderDetail>orderdetails,將訂單的明細對映到orderdetails;
在OrderDetail中新增items屬性,將訂單明細所對應的商品對映到items。
<!-- 多對多查詢 -->
<resultMap type="com.zwp.po.User" id="UserAndItemsResultMap">
<!-- 配置使用者資訊 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 配置訂單資訊:使用者對訂單:一對多關係:collection -->
<collection property="orders" ofType="com.zwp.po.Orders">
<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" />
<!-- 配置訂單詳情資訊:訂單對訂單詳情:一對多關係:collection -->
<collection property="orderdetail" ofType="com.zwp.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
<!-- 配置商品資訊:訂單詳情對商品:一對一:association -->
<association property="items" javaType="com.zwp.po.Items">
<id column="itemsid" property="id"/>
<result column="name" property="name"/>
<result column="price" property="price"/>
<result column="detail" property="detail"/>
<result column="items_creatime" property="creatime"/>
</association>
</collection>
</collection>
</resultMap>
<select id="findUserAndItemsResultMap" resultMap="UserAndItemsResultMap">
select orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.id itemsid,
items.name,
items.price,
items.detail,
items.creatime items_creatime
from orders,user,orderdetail,items
where orders.user_id=user.id and orderdetail.orders_id=orders.id and items_id=items.id
</select>
public class User {
private Integer id;
private String username;
private String birthday;
private String sex;
private String address;
private List<Orders> 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> orderdetail;
}
public class Orderdetail {
private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;
private Items items;
}
public class Items {
private Integer id;
private String name;
private Float price;
private String pic;
private Date creatime;
private String detail;
}
(4)總結:
①resultMap:使用association和collection完成一對一和一對多高階對映,用於對結果有特殊的對映要求。
②association:
作用:將關聯查詢資訊對映到一個pojo物件中。
場合:為了方便查詢關聯資訊,可以使用association將關聯訂單資訊對映為使用者物件的pojo屬性中,比如:查詢訂單及關聯使用者資訊。
使用resultType無法將查詢結果對映到pojo物件的pojo屬性中,根據對結果集查詢遍歷的需要選擇使用resultType還是resultMap。
③collection:
作用:將關聯查詢資訊對映到一個list集合中。
場合:為了方便查詢遍歷關聯資訊可以使用collection,將關聯資訊對映到list集合中,比如:查詢使用者許可權範圍模組下及模組下的選單,可以使用collection將其許可權模組對映到許可權模組list中,將選單列表對映到許可權模組物件的選單list屬性中,這樣做的目的也是方便對查詢結果進行遍歷查詢。
如果使用resultType無法將查詢結果對映到List集合中。
9、延時載入:
resultMap的association和collection具備延時載入功能。
延時載入:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高資料庫效能,因為查詢單表要比關聯查詢多表速度要快。
(1)使用association中的select指定延遲載入去執行的statement的id
<!-- 延遲載入:
查詢使用者訂單資訊:使用者資訊要求延遲載入 -->
<resultMap type="com.zwp.po.Orders" id="OrderUserLazyLoading">
<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,如果不在同一個namespace,需要加上namespace
column:表示關聯的欄位-->
<association property="user" javaType="com.zwp.po.User" select="findUserById" column="user_id">
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="sex" property="sex" />
<result column="address" property="address" />
<result column="birthday" property="birthday" />
</association>
</resultMap>
<!-- 不可以使用resultType,因為resultType沒有延遲載入功能 -->
<select id="findOrderUserLazyLoading" resultMap="OrderUserLazyLoading">
select * from orders
</select>
<!-- 查詢訂單關聯查詢使用者,使用者資訊需要延時載入 -->
<select id="findUserById" parameterType="int" resultType="com.zwp.po.User">
SELECT * FROM USER WHERE id=#{id}
</select>
(2)延遲載入配置:
Mybatis預設沒有開啟延時載入,需要在SqlMapperConfig.xml中的setting配置。
<!-- settings:配置全域性變數 -->
<settings>
<!-- 開啟延遲載入開關 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 將積極載入改為消極載入,即按需載入 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
(3)總結:
使用延時載入方法,先去查詢簡單的sql(最好是單表,也可以關聯查詢),再去按需要載入關聯查詢的其他資訊。
10、一級快取:(mybatis預設支援一級快取)
快取:提高系統的效能,減少資料庫的壓力。
11、二級快取:(預設不開啟)
(1)開啟二級快取:
①在核心配置檔案SqlMapConfig.xml中加入:
<!-- settings:配置全域性變數 -->
<settings>
<!-- 開啟二級快取 -->
<setting name="cacheEnabled" value="true"/>
</settings>
②在xxxMapper.xml檔案中開啟二級快取,xxxMapper.xml下的sql執行完會儲存到他的快取區域(HashMap)
<mapper namespace="com.zwp.mapper.OrdersMapperCustom">
<!-- 開啟本mapper下的二級快取
<cache/>
</mapper>
(2)useCache配置:禁用二級快取:
在statement中設定useCache=false可以禁用當前select語句的二級快取,即每次查詢都會發出sql去查詢,預設是true,即改sql使用二級快取。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
場景:針對每次查詢都需要更新的資料sql,要設定成useCache=false,禁用二級快取。
(3)重新整理快取(清空快取):
設定statement配置中的flushCache="true"屬性,預設情況下為true即重新整理快取,如果改成false則不會重新整理。
<insert id="insertUser" parameterType="com.zwp.domain.User" flushCache="true">
場景:一般情況下執行完commit操作都需要重新整理快取,flushCache=true表示重新整理快取,這樣可以避免資料庫髒讀。
12、分佈快取:mybatis整合ehcache:
分佈快取:可以實現對快取資料進行集中管理。
(1)Mybatis無法實現分散式快取,需要和其他分散式快取框架進行整合。
(2)整合方法:
mybatis提供了一個cache介面,如果要實現自己的快取邏輯,實現cache介面開發即可。
mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個Cache介面的實現類。
(3)加入jar包依賴:
(4)整合ehcache:
<mapper namespace="com.zwp.mapper.OrdersMapperCustom">
<!-- 開啟本mapper下的二級快取
type:指定cache介面的實現的型別,mybatis預設使用PerpetualCache-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(5)加入ehcache的配置檔案:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="D:\develop\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
13、mybatis和spring整合開發:
需要spring通過單例方式管理SqlSessionFactory。
spring和mybatis整合生成代理物件,使用SqlSessionFactory建立SqlSession,持久層的mapper都需要由spring進行管理。
步驟:
(1)匯入jar包依賴:
(2)sqlSessionFactory配置:
在applicationContext.xml配置sqlSessionFatory,sqlSessionFatory在mybatis和spring的整合包下。
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1.載入資料庫配置檔案 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 2.配置連線池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 3.建立會話工廠sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 載入mybatis的配置檔案 -->
<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
<!-- 配置資料來源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
(3)①原始dao開發:
--User.xml檔案:
<mapper namespace="test">
<select id="findUserById" parameterType="int" resultType="com.zwp.ssm.po.User">
SELECT * FROM USER WHERE id=#{id}
</select>
</mapper>
在SqlMapConfig.xml檔案中載入對映檔案:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 載入對映檔案 -->
<mappers>
<!-- 通過resource載入單個對映檔案 -->
<mapper resource="sqlmap/User.xml"></mapper>
</mappers>
</configuration>
--Dao:(實現類繼承SqlSessionDaoSupport)public interface UserDao {
//根據id查詢使用者
public User findUserById(int id) throws Exception;
}
DaoImpl介面實現類需要注入sqlSessionFactory,通過spring進行注入:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao{
//繼承SqlSessionDaoSupport
//父類已經定義SqlSessionFactory物件和set方法,不需要重新寫
@Override
public User findUserById(int id) throws Exception{
SqlSession sqlSession=this.getSqlSession();//不需要手動關閉sqlSession
System.out.println(sqlSession);
User user=sqlSession.selectOne("test.findUserById",id);
return user;
}
}
通過spring建立介面的bean物件:
<!--原始dao介面 -->
<bean id="userDao" class="com.zwp.Dao.UserDaoImpl">
<!-- sqlSessionFactory不能寫錯 -->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
(3)②mapper代理開發:--mapper.xml和mapper.java:
public interface UserMapper {
/*
(1)mapper.java介面中方法名和mapper.xml中的statement的id一致
(2)mapper.java介面中方法的輸入引數型別和mapper.xml中statement的parameterType指定的型別一致
(3)mapper.java介面中方法的返回值型別和mapper.xml中statement的resultment中resultType指定型別一致。
*/
//根據id查詢使用者
public User findUserById(int id) throws IOException;
}
<?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="com.zwp.ssm.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.zwp.ssm.po.User">
SELECT * FROM USER WHERE id=#{id}
</select>
</mapper>
----通過mapperFactoryBean建立代理物件(此方法存在問題):
<!-- mapper介面 -->
<!-- MapperFactoryBean:根據mapper介面生成代理物件 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- mapperInterface指定mapper介面 -->
<property name="mapperInterface" value="com.zwp.ssm.mapper.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
此方法的問題:如果有很多個mapper,需要針對每個mapper進行單獨配置。
解決方法:通過MapperScannerConfigure進行mapper批量掃描。
<!-- mapper批量掃描,從mapper包中掃描出mapper,自動建立代理物件並且在spring容器中註冊
遵循規範:需要mapper介面類名和mapper.xml對映檔名稱一致,且在同一目錄下
自動掃描出來的mapper的bean的id為類名(首字母小寫)-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定掃描的包名 -->
<!-- 如果掃描多個包,每個包中間使用半形逗號隔開 -->
<property name="basePackage" value="com.zwp.ssm.mapper"></property>
<!-- sqlSessionFactoryBeanName不能寫成sqlSessionFactory,不然會導致連線不上資料庫 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>