最近一個項目用到mybatis,花了一點時間看了官方文檔,後面就搭建起了架構,着手進行開發,mybatis上手很容易,但是有一些小的細節的注意(下文說明),否則錯誤很難查找,對于用慣了Hibernate的開發人員來說,使用mybatis可能可能需要加強SQL。大概說一下mybatis優缺點,歡迎補充。
mybatis優點:
1. 易于上手和掌握。
2. sql寫在xml裡,便于統一管理和優化。
3. 解除sql與程式代碼的耦合。
4. 提供映射标簽,支援對象與資料庫的orm字段關系映射
5. 提供對象關系映射标簽,支援對象關系組建維護
6. 提供xml标簽,支援編寫動态sql。
mybatis缺點:
1. sql工作量很大,尤其是字段多、關聯表多時,更是如此。
2. sql依賴于資料庫,導緻資料庫移植性差。
3. 由于xml裡标簽id必須唯一,導緻DAO中方法不支援方法重載。
4. 字段映射标簽和對象關系映射标簽僅僅是對映射關系的描述,具體實作仍然依賴于sql。(比如配置了一對多Collection标簽,如果sql裡沒有join子表或查詢子表的話,查詢後傳回的對象是不具備對象關系的,即Collection的對象為null)
5. DAO層過于簡單,對象組裝的工作量較大。
6. 不支援級聯更新、級聯删除。
7. 編寫動态sql時,不友善調試,尤其邏輯複雜時。
8. 提供的寫動态sql的xml标簽功能簡單(連struts都比不上),編寫動态sql仍然受限,且可讀性低。
9. 使用不當,容易導緻N+1的sql性能問題。
10. 使用不當,關聯查詢時容易産生分頁bug。
11. 若不查詢主鍵字段,容易造成查詢出的對象有“覆寫”現象。
12. 參數的資料類型支援不完善。(如參數為Date類型時,容易報沒有get、set方法,需在參數上加@param)
13. 多參數時,使用不友善,功能不夠強大。(目前支援的方法有map、對象、注解@param以及預設采用012索引位的方式)
查詢結果說明:
mybatis可以通過配置傳回Java對象,例如查詢一個使用者對象,下面為mapper配置,select查詢可以指定傳回resultType或者resultMap。
<?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.sclead.dao.mapper.UserMapper">
<resultMap type="com.sclead.dao.entity.User" id="userMap">
<id property="userId" column="user_id" />
<result property="depId" column="dep_id" />
<result property="userName" column="user_name" />
<result property="realName" column="real_name" />
<result property="password" column="user_pw" />
<result property="createTime" column="user_create_time" />
<result property="state" column="user_state" />
<result property="note" column="user_note" />
<!-- 關聯查詢機關資訊 -->
<association property="department" resultMap="com.sclead.dao.mapper.DepartmentMapper.depMap"/>
<!-- 關聯查詢角色資訊 -->
<collection property="roles" resultMap="com.sclead.dao.mapper.RoleMapper.roleMap"/>
</resultMap>
<!-- 公用sql -->
<sql id="baseSql">
select u.*,d.*,r.*
from ly_user u
left join ly_department d on u.dep_id = d.dep_id
left join ly_user_role ur on ur.user_id = u.user_id
left join ly_role r on r.role_id = ur.role_id
</sql>
<!-- 查詢一個使用者 -->
<select id="getUserById" parameterType="int" resultMap="userMap">
<include refid="baseSql"/> where user_id=#{userId}
</select>
<!-- 查詢所有記錄 -->
<select id="getAllUser" resultMap="userMap">
<include refid="baseSql"/>
</select>
</mapper>
如果實體類屬性和資料庫中字段名一樣,則可以直接指定resultType,值為定義的實體類,mybatis會預設建立resultMap指定對應關系,resultType=“User”,User需在配置中定義,如下段代碼,查詢成功後傳回User對象。
<?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>
<typeAliases>
<typeAlias type="com.sclead.dao.entity.User" alias="User"/>
<!--
<mappers>
<mapper resource="com/sclead/dao/mapper/xml/user-mapper.xml"/>
</mappers>
-->
</configuration>
如果實體類屬性和資料庫中字段名稱不相同,必須在配置中聲明resultMap映射,指定字段和屬性對應關系,查詢時指定resultMap,查詢成功後會傳回User對象,或者是User集合。
三張表關聯查詢,這裡定義三張表,A、B、C,B關聯A,C關聯B,在查詢A的時候同時查詢B和C,在A實體類中定義B對象集合,在B實體類中定義C對象集合。
A表{a_id,......},B表{b_id,b_a_id,......},C表{c_id,c_b_id,......}
public class A {
private int aId;
private List<B> bs;
public int getaId() {
return aId;
}
public void setaId(int aId) {
this.aId = aId;
}
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
}
public class B {
private int bId;
private int baId;
private List<C> cs;
public int getBId() {
return bId;
}
public void setBId(int bid) {
this.bId = bid;
}
public int getBaId() {
return baId;
}
public void setBaId(int baId) {
this.baId = baId;
}
public List<C> getCs() {
return cs;
}
public void setCs(List<C> cs) {
this.cs = cs;
}
}
public class C {
private int cId;
private int cbId;
public int getcId() {
return cId;
}
public void setcId(int cId) {
this.cId = cId;
}
public int getCbId() {
return cbId;
}
public void setCbId(int cbId) {
this.cbId = cbId;
}
}
<?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.abc.AMapper">
<resultMap type="com.abc.A" id="aMap">
<id property="a_Id" column="a_id" />
<!-- 其它字段略 -->
<!-- 關聯查詢B資料 -->
<collection property="bs" resultMap="com.sbc.BMapper.bMap"/>
</resultMap>
<!-- 公用sql -->
<sql id="baseSql">
select * from ly_a a
left join ly_b b on a.a_id = b.b_a_id
left join ly_c c on b.b_id = c.c_b_id
</sql>
<!-- 查詢所有記錄 -->
<select id="getAllA" resultMap="aMap">
<include refid="baseSql"/>
</select>
<!-- 查詢一個 -->
<select id="getAById" parameterType="int" resultMap="aMap">
<include refid="baseSql"/> where a.a_id=#{a_id}
</select>
</mapper>
<?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.abc.BMapper">
<resultMap type="com.abc.B" id="aMap">
<id property="bId" column="b_id" />
<id property="baId" column="b_a_id" />
<!-- 其它字段略 -->
<!-- 關聯查詢C資料 -->
<collection property="cs" resultMap="com.sbc.CMapper.cMap"/>
</resultMap>
</mapper>
<?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.abc.CMapper">
<resultMap type="com.abc.C" id="cMap">
<id property="cId" column="c_id" />
<id property="cbId" column="c_b_id" />
<!-- 其它字段略 -->
</resultMap>
</mapper>
在調用getAById或者getAllA,傳回A對象或者A集合,A對象中已經封裝了關聯的B對象,B對象中已經封裝了關聯的C對象。
mybatis的關聯查詢還有其他集中配置方式,這裡隻介紹多級關聯查詢,