MyBatis映射文件
Editor:SimpleWu
MyBatis真正的核心是在映射文件,由於它的異常強大,如果拿它和相同功能的JDBC代碼相比,你會發現它省掉了將近95%的代碼。
MyBatis實體類映射文件
MyBatis有一下幾個頂級元素
select:映射查詢語句
<select id="findAll" resultType="com.simple.mybatis.entitys.Employee">
select * from tal_employee
</select>
insert:映射插入語句
使用#{參數屬性名}賦值
<insert id="saveEmp" parameterType="com.simple.mybatis.entitys.Employee">
insert into tal_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>
update:映射修改語句
<update id="updateEmp" parameterType="com.simple.mybatis.entitys.Employee">
update tal_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
delete:映射刪除語句
<delete id="deleteEmp" parameterType="Integer">
delete from tal_employee where id=#{id}
</delete>
sql:可以被其它sql語句重用的sql語句。
resultMap:是最復雜也是最強大的元素,用來和數據庫表和實體類進行映射。
<resultMap type="com.simple.mybatis.entitys.Employee" id="Employee">
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
</resultMap>
<!-- 返回外部resultMap格式的類型。-->
<select id="findAll" resultMap="Employee">
select id,last_name AS lastName,email,gender from tal_employee
</select>
cache:給定命名空間的緩存配置。
cache-ref:其它命名空間緩存配置的引用。
我們結合第一章,使用之前與數據庫互交的方式:
sqlSession.selectList("命名空間.select標簽ID");
sqlSession.selectOne("命名空間.select標簽ID");
sqlSession.update("命名空間.update標簽ID", "Object類型參數");
sqlSession.delete("命名空間.delete標簽ID", "Object類型參數");
sqlSession.insert("命名空間.insert標簽ID", "Object類型參數");
增刪改必須提交事務:sqlSession.commit();
使用XML映射文件方式來在執行方法時,由於是通過字符串方式來調用方法,對類型的約束也不是強制性的,可讀性較差,這是早期ibatis的調用方式。除此之外Mybatis還提供另外兩種實現方式
MyBatis注解方式
-
@Select:用來修飾使用查詢語句的方法
-
@Insert:用來修飾使用增加語句的方法、
-
@Update:用來修飾使用修改修改的方法。
-
@Delete:用來修飾使用刪除語句的方法。
public interface EmployeeAnnotation {
@Select("select id,last_name AS lastName,email,gender from tal_employee")
ListfindAll(); @Insert("insert into tal_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})") int saveEmp(Employee employee); @Update("update tal_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}") boolean updateEmp(Employee employee); @Delete("elete from tal_employee where id=#{id}") long deleteEmp(Integer id);}
然后我們需要在mybatis主配置文件中映射這個注解接口
<!-- 映射注解類 -->
<mapper class="com.simple.mybatis.dao.EmployeeAnnotation"/>
@Test
public void testAnnotation(){
SqlSession session = sqlSessionFactory.openSession();
EmployeeAnnotation employeeAnnotation = session.getMapper(EmployeeAnnotation.class);
Employee employee = new Employee("測試注解增加", "email", "男");
employeeAnnotation.saveEmp(employee);
//提交事務
session.commit();
session.close();
}
使用注解雖然更加方便,但是配置復雜sql語句時較為復雜。所以很多時候采用兩種相結合的方式。
Mybatis接口編程
我們首先添加一個接口
public interface EmployeeMapper {
List<Employee> findAll();
int saveEmp(Employee employee);
boolean updateEmp(Employee employee);
long deleteEmp(Integer id);
}
實體類映射文件命名空間(namespace)必須對應接口全名,並且里面的方法需要對應里面增刪改查標簽ID完成綁定
<?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.simple.mybatis.dao.EmployeeMapper">
<resultMap type="com.simple.mybatis.entitys.Employee" id="Employee">
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
</resultMap>
<!-- 返回外部resultMap格式的類型。-->
<select id="findAll" resultMap="Employee">
select id,last_name AS lastName,email,gender from tal_employee
</select>
<insert id="saveEmp" parameterType="com.simple.mybatis.entitys.Employee">
insert into tal_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>
<update id="updateEmp" parameterType="com.simple.mybatis.entitys.Employee">
update tal_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<delete id="deleteEmp" parameterType="Integer">
delete from tal_employee where id=#{id}
</delete>
</mapper>
並且必須在MyBatis主配置文件中加載這個映射文件
<mappers>
<mapper resource="com/simple/mybatis/entitys/EmployeeMapper.xml" />
</mappers>
使用方式:
@Test
public void test4(){
SqlSession session = sqlSessionFactory.openSession();
//獲取接口。這個接口mybatis會幫我創建代理實現類完成接口與XML映射的綁定
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
employeeMapper.deleteEmp(1);
session.commit();
session.close();
}
獲取插入后的自動增長主鍵
useGeneratedKeys參數只針對 insert 語句生效,默認為 false。當設置為 true 時,表示如果插入的表以自增列為主鍵,則允許 JDBC 支持自動生成主鍵,並可將自動生成的主鍵返回。 keyProperty是要封裝給parameterType="com.simple.mybatis.entitys.Employee中的主鍵ID屬性名
<insert id="saveEmpGetKey" parameterType="com.simple.mybatis.entitys.Employee"
useGeneratedKeys="true" keyProperty="id"
>
insert into tal_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>
@Test
public void saveEmpGetKey(){
SqlSession session = sqlSessionFactory.openSession();
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
Employee employee = new Employee("自動封裝主鍵", "email", "男");//增加之前主鍵沒賦值
employeeMapper.saveEmpGetKey(employee);
session.commit();//提交事務
System.out.println("增加后主鍵有值: " + employee.getId());
session.close();//關閉資源
}
Mybatis參數規則
一個參數時:
直接使用#{xxx}來訪問參數即可。參數名稱任意。例如#{a},#{b}都可以訪問參數值。如果是集合(Collection,List,Set)類型,使用集合名稱小寫作為key值。
必須接口方法:
long deleteEmp(Integer emp);
映射文件:
<delete id="deleteEmp" parameterType="Integer">
delete from tal_employee where id=#{id}
</delete>
在這里可以看到我們是沒有給這個賦值的,#{id}我這個屬性名也不叫id而是emp。所以一個參數是名字可以任意如果是傳入的集合類型
傳入Map參數時取值:
//接口有方法:long deleteEmp(Map<String, Object> map);
<delete id="deleteEmp" parameterType="Map">
delete from tal_employee where id=#{empID}
</delete>
我們這里取值是empID,這想想都知道創建一個map.put(“empID”,1)作為參數傳入方法調用就行了
多個參數時:
- 默認:就不能使用#{xxx}來訪問參數了。當有多個參數時,mybatis會將參數封裝為一個map集合,只能通過#{下標}或者#{paramN}的方式來獲取值。
- 命名參數:明確指定封裝參數時手動指定map的key。通過在接口方法上添加@Param("key")來指定參數名稱。
- 實體類:如果多個參數是封裝好的業務類,那么直接傳入業務對象即可。可以通過#{屬性名}獲取屬性即可。Map:如果多個參數不是已封裝的業務類,那么傳入map集合即可。通過#{key}獲取對應的value值。
{}和${}
MyBatis中,除了可以使用#{}來獲取參數的值意外,還可以使用${}來獲取參數的值。
區別:#{}:會以預編譯的方式,將參數生成到sql語句中。${}:會直接將值生成到sql語句。
大多數情況下,我們都是以#{}來獲取參數,但是有些地方不支持占位符的地方那么就可以使用${}來獲取參數,比如表名。
resultType返回類型
- 對於增刪改的方法,Mybatis會自動對結果進行封裝,返回int,Boolean,long都可以。
- 對於返回實體對象和List集合,resultType可以設置為實體類型。
- 如果返回單個對象,也可以封裝為Map<String,Object>,resultType設置為map。
- 如果返回Map集合對象,比如Map<String,Student>,resultType設置為Student類型,通過@MapKey("id")注解來修飾放入Map中的key值。
ResultMap標簽介紹
ResultMap的常用子標簽
- id:映射主鍵。result:映射普通列。
- association:復雜結果映射。
- collection:復雜類型的集合映射。
- constructor:構造函數注入。
前面我們的案例我們數據庫的字段與實體類的屬性基本上一致,或者不一致使用取別名方案解決,可是每條語句都去取別名是非常麻煩的,ResultMap這個標簽就很好的解決數據庫與實體類字段不對應的問題
我們新創建一個部門表:
CREATE TABLE tal_dept(
d_id INT PRIMARY KEY AUTO_INCREMENT,
d_name VARCHAR(50)
);
對應實體類:
public class Dept {
private Integer id;
private Integer name;
}
如果我們查詢語句直接使用resultType="com.simple.mybatis.entitys.Dept那么毫無疑問我們的實體類中名字不對應是賦值為null的,我們可以ResultMap映射實體類與數據庫的對應關系
<!--配置返回結果,配置數據庫字段與類屬性的映射-->
<resultMap type="com.simple.mybatis.entitys.Dept" id="DeptResult">
<id column="d_id" property="id"/>
<result column="d_name" property="name"/>
</resultMap>
<!--使用resultMap使用指向上面返回結果的ID-->
<select id="getDeptOne" parameterType="Integer" resultMap="DeptResult">
select * from tal_dept where d_id = #{id}
</select>
關系映射
在數據庫中,許多數據是分布在多個表中的,有時候需要將多個表的數據關聯起來進行查詢。那么在ORM框架中,我們需要處理數據表的映射關系。
常見的映射關系:
- 關聯屬性映射association
- 映射collection映射
result方式映射屬性
這種方式並不推薦,沒有什么重用性
<resultMap type="com.simple.mybatis.entitys.Employee" id="EmployeeOrDept">
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<!-- 指定Employee中Dept對象屬性 -->
<result property="dept.id" column="d_id"/>
<result property="dept.name" column="d_name"/>
</resultMap>
<select id="getEmployeeOrDeptAll" resultMap="EmployeeOrDept">
select * from tal_employee e inner join tal_dept d on e.d_id=d.d_id
</select>
association映射(分步查詢)
多對一,查詢員工獲取員工中的部門:
<resultMap type="com.simple.mybatis.entitys.Employee" id="EmployeeOrDept">
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<!-- property指定實體類中對象dept 指定發過去的參數column="d_id"
select指定com.simple.mybatis.dao.DeptMapper映射文件中getDeptOne查詢方法
-->
<association property="dept" column="d_id"
select="com.simple.mybatis.dao.DeptMapper.getDeptOne"></association>
</resultMap>
<!--resultMap指定使用上面定義的返回結果-->
<select id="getEmployeeOrDeptAll" resultMap="EmployeeOrDept">
select * from tal_employee e inner join tal_dept d on e.d_id=d.d_id
</select>
Collection查詢
一對多,獲取部門同時部門中員工也獲取:
<!--部門映射-->
<resultMap type="com.simple.mybatis.entitys.Dept" id="DeptResult">
<id column="d_id" property="id"/>
<result column="d_name" property="name"/>
<!--property指定Dept中集合屬性名 ofType指定集合中屬性類型 -->
<collection property="Employees" ofType="com.simple.mybatis.entitys.Employee" >
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
</collection>
</resultMap>
<select id="getDeptAll" resultMap="DeptResult">
select * from tal_dept d left join tal_Employee e on d.d_id = e.d_id
</select>
Collection嵌套查詢
一對多分布查詢
<resultMap type="com.simple.mybatis.entitys.Dept" id="DeptOrEmployee">
<id column="d_id" property="id"/>
<result column="d_name" property="name"/>
<!--select指定com.simple.mybatis.dao.EmployeeMapper映射文件中的getEmployeeByDeptId-->
<collection property="Employees" column="d_id" ofType="com.simple.mybatis.entitys.Employee"
select="com.simple.mybatis.dao.EmployeeMapper.getEmployeeByDeptId">
</collection>
</resultMap>
<!--com.simple.mybatis.dao.EmployeeMapperXML中方法-->
<select id="getEmployeeByDeptId" parameterType="Integer" resultType="com.simple.mybatis.entitys.Employee">
select * from tal_employee where d_id = #{id}
</select>
關聯查詢和分步查詢的區別:
- 關聯查詢一次將多個表的數據查詢出來,分步查詢通過多次查詢獲取查詢結果。
- 配置文件不同,關聯查詢需要定義額外的映射,分步查詢需要定義外鍵列,和查詢的select方法。
- 關聯查詢不支持延遲加載,分步查詢支持延遲加載。fetchType="lazy"
延遲加載
剛才分布查詢是一次將結果查詢出來,為了提高效率,mybatis還支持延遲加載技術,等需要用到對象時才進行查詢。
在mybatis主配置文件中配置:
<!– 通過全局配置文件設置延遲加載-->
<settings>
<!-- 全局啟用或禁用延遲加載。當禁用時,所有關聯對象都會即時加載。 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 當啟用時,有延遲加載屬性的對象在被調用時將會完全加載任意屬性。否則,每種屬性將會按需要加載。 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
設置級聯延遲加載
<!– 設置延遲加載屬性-->
<association fetchType="lazy"/>
