1. 程式人生 > >資料持久層框架Mybatis

資料持久層框架Mybatis

目錄

1.軟體開發3層架構

2.什麼是框架?

3.傳統JDBC開發的不足?

4.什麼是Mybatis?

5.Mybatis入門案例?

6.Mybatis增刪改查案例?

7.Mybatis代理實現DAO開發?

8.Mybatis高階對映


1.軟體開發3層架構

 軟體開發的3層架構是Java提出分層開發(經典開發)模式,即將軟體開發分為表示層、業務邏輯層、資料持久層開發,互相獨立,各不影響。

表示層

  1. 由與使用者進行互動的元件和容器構成(接收請求並響應請求),比如servlet。
  2. 分包:action/web/servlet。
  3. 處理表示層的框架SprintMVC/Struts2。

業務邏輯層

  1. 由業務處理的元件組成。
  2. 分包:service/biz。
  3. 處理業務邏輯層的框架Spring Framwork。

資料持久層

  1. 實現與資料庫直接互動,比如jdbc。
  2. 分包:dao。
  3. 處理資料持久層的框架Mybatis/Hibernate/JdbcTemplate。

2.什麼是框架?

定義

  1. 將重複性的、繁瑣的內容封裝起來的一套程式。
  2. 框架使開發人員能夠將更多地精力放在業務的分析和理解上。

好處

  1. 簡化開發,配置即可
  2. 遮蔽細節
  3. 提高開發效率

3.傳統JDBC開發的不足?

在引入框架之前,我們都使用JDBC完成資料持久層的開發,但有下面幾處不足:

  1. 資料庫驅動註冊和連接獲取的硬編碼問題(配置檔案解決)
  2. SQL預編譯時賦值和結果集封裝的時候繁瑣(反射解決 ORM,即物件關係對映)
  3. 頻繁開啟和釋放連線消耗資源(連線池解決)

4.什麼是Mybatis?

官網http://www.mybatis.org/mybatis-3

  1. MyBatis 是一款優秀的持久層框架
  2. MyBatis只需要關注SQL語句、輸入引數和輸出結果的對映
  3. MyBatis是一個ORM框架。ORM表示物件關係對映,解決面向物件程式設計模型和關係型資料庫模型之間的對映問題。

5.Mybatis入門案例?

步驟

  1. 準備jar包,mysql驅動包(mysql-connector-java-5.1.45-bin.jar)和Mybatis核心jar包(mybatis-3.4.5.jar)
    1. Mybatis原本是apache專案,原來被叫做 ibatis
    2. 後來移植到google code,改名為mybatis
    3. 現在在github(開源軟體託管平臺)脫光,官網:https://github.com/mybatis/mybatis-3/releases
  2. 建立Java專案,並將前面的2個jar包匯入專案,如下圖所示,其中lib目錄手動建立,2個jar拷貝到lib,並build path二者。
  3. 建立並編寫2種配置檔案
    1. 全域性配置檔案:4個引數配置連線池、進行事務管理
    2. 對映配置:定製化sql語句、輸入引數與輸出結果對映
    3. 配置方式:
      1. 在Java專案下建立source folder(與src目錄同級並且一樣的目錄)用於存放配置檔案
      2. 全域性配置檔案和對映配置檔案的編寫方式見下方程式碼
  4. 讀取配置並執行測試
    1. Sqlsession:面向開發者的一個介面,提供了傳送sql命令的方法。
    2. SqlSessionFactory:session工廠,建立和管理session物件的。

程式碼

專案目錄結構

pojo中User類

package pojo;
public class User {
	private int uid;
	private String uname;
	private String password;
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String toString() {
		return "User [uid=" + uid + ", " + (uname != null ? "uname=" + uname + ", " : "") + (password != null ? "password=" + password : "") + "]";
	}
}

Java屬性檔案jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///bd1808?useSSL=true
user=root
password=root

全域性配置檔案mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 這段主要作用就是約束當前文件怎麼去寫
	xml描述檔案:dtd/schema,作用是約束標籤
 -->
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- 載入外部屬性檔案 -->	
  <properties resource="jdbc.properties"></properties>
  <!-- 類型別名:標籤時候順序的,這個必須寫到properties標籤的後面 -->
  <typeAliases>
 	<!-- 單個類型別名 -->
  	<!-- <typeAlias alias="user" type="pojo.User"/> -->
  	<!-- 批量起別名:別名就是類名,而且不區分大小寫 -->
  	<package name="pojo" />
  </typeAliases>
  <!-- 環境context:複數 
  	mysql、oracle、db2
  	本地環境、預上線環境、線上環境
  	default:預設使用環境
  -->
  <environments default="development">
  	<!-- 環境:id是唯一表示 -->
    <environment id="development">
      <!-- 事務管理器:JDBC,mybatis框架的事務管理器 -->
      <transactionManager type="JDBC"/>
      <!-- 資料來源:POOLED連線池(儲存連線) 其他連線池:dbcp/c3p0/druid/jdbctemplate -->
      <dataSource type="POOLED">
      	<!-- name屬性的值都是固定的,必須這麼寫,否則框架無法識別 -->
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!-- 對映器 :配置對映檔案路徑-->
  <mappers>
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

 對映配置檔案UserMapper.xml

<?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">
<!-- namespace:名稱空間(字串)
	隔離sql
	這裡abcd隨便寫的
	 -->  
<mapper namespace="ahsb1">
  <!-- 
  	根據uid查詢user的物件
  	一個標籤就代表一個Statement的物件
  	#{}:代表佔位符,在jdbc裡面就是?的意思
  	#{}中的id:關鍵字,即輸入引數
  		引數是簡單型別:比如基本資料型別和字串,可任意
  		引數是用型別:屬性名稱
  	id:標籤的唯一表示
  	parameterType:輸入引數型別(可選),不寫就會進行型別推斷
  	resultType:	輸出結果型別(單個結果的型別)
   -->
  <select id="selectUser" resultType="User">
    select * from user where uid = #{uid}
  </select>
  <!-- 查詢所有 -->
  <select id="selectAll" resultType="User">
  	select * from user
  </select>
  <!-- 模糊查詢
  	${}:表示連線
  		關鍵字與輸入引數的型別有關:
  			簡單型別:value
  			引用型別:屬性名稱
   -->
  <select id="selectLikeName" resultType="User">
  	select * from user where uname like '%${value}%'
  </select>
  <!-- 插入 -->
  <insert id="insert1" >
  	insert into user(uname,password) values(#{uname},#{password})
  </insert>  
</mapper>

測試類Test

package test;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import pojo.User;
public class Test {
	public static void main(String[] args) throws IOException {
		//讀取配置檔案
		Reader reader = Resources.getResourceAsReader("mybatis.xml");
		SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		//傳送定製好的sql
		//SqlSession session = ssf.openSession();//關閉自動提交
		SqlSession session = ssf.openSession(true);//不關閉自動提交
		/*User user = session.selectOne("ahsb.selectUser", 1);
		System.out.println(user);*/
		//查詢所有
		/*List<User> users = session.selectList("ahsb.selectAll");
		System.out.println(users);*/
		//模糊查詢
		/*List<User> users = session.selectList("ahsb.selectLikeName","s");
		System.out.println(users);*/
		User user = new User();
		user.setUname("zl");
		user.setPassword("123");
		int rows = session.insert("ahsb1.insert1", user);
		//手動事務提交
		//session.commit();
		System.out.println(rows);
		session.close();
	}
}

6.Mybatis增刪改查案例?

參見上面第5部分的程式碼,注意點如下:

  1. 查詢所有
  2. 模糊查詢:知識點是${ }連線佔位符。這裡匯入了2個檔案,即log4j-1.2.17.jar 和 log4j.properties(與mybatis.xml同目錄)
  3. 插入資料:知識點是執行完sql語句後手動提交事務或獲取session物件時選擇不關閉自動提交的工廠方法。

7.Mybatis代理實現DAO開發?

原理:mapper的介面+mapper的對映

要求

  1. mapper介面和mapper的對映同包同名(註冊對映時方便)
  2. mapper對映檔案的namespace和介面的完全限定名保持一致
  3. 標籤的id和方法的名稱保持一致
  4. 標籤的輸入引數型別和方法的引數型別一致
  5. 標籤的輸出結果型別和方法的返回值型別一致

程式碼(配置檔案版)

User類

package pojo;
public class User {
	private int uid;
	private String uname;
	private String password;
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String toString() {
		return "User [uid=" + uid + ", " + (uname != null ? "uname=" + uname + ", " : "") + (password != null ? "password=" + password : "") + "]";
	}
}

UserMapper介面

package mapper;
import java.util.List;
import pojo.User;
public interface UserMapper {
	List<User> queryAll();
	User queryById(int uid);
}

UserMapper.xml檔案(即UserMapper介面的對映檔案,存放在mapper包下面哈,和UserMapper同包同名)

<?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="mapper.UserMapper">
	<select id="queryAll" resultType="User">
		select * from user
	</select>
	<select id="queryById" resultType="User">
		select * from user where uid=#{}
	</select>
</mapper>

Test類(獲取配置,執行程式碼)

package test;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import mapper.UserMapper;
public class Test {
	public static void main(String[] args) throws IOException {
		Reader reader = Resources.getResourceAsReader("mybatis.xml");
		SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		SqlSession session = ssf.openSession();
		UserMapper mapper = session.getMapper(UserMapper.class);
		System.out.println(mapper.queryAll());
		session.close();
	}
}

程式碼(註解配置版)(關係比較簡單時使用)

//在mapper介面中通過註解配置,就不需要mapper配置檔案了!
//其他程式碼和配置仍然保持不變
public interface UserMapper {
	@Select("select * from user")
	List<User> queryAll();
	@Select("select * from user where uid=#{uid}")
	User queryById(int uid);
}

8.Mybatis高階對映

需求描述:查詢部門及其部門下的所有員工。

對映分類:一對多和一對一

一對多:

  1. resultType特點
    1. 結果集中欄位名稱和類屬性名稱完全一致,此時將對映成功。
    2. 結果集中的欄位和類的屬性名稱部分一致,部分對映成功。
    3. 結果集中的欄位和類的屬性名稱都不一致,此時不會建立物件。
  2. 一對多:使用resultMap+collection標籤

一對一:使用resultMap+association標籤

程式碼實現

專案目錄結構

pojo類:

package pojo;

import java.util.List;

public class Dept {
	private int deptno;
	private String dname;
	private String loc;
	private List<Emp> emps;
	public int getDeptno() {
		return deptno;
	}
	public void setDeptno(int deptno) {
		this.deptno = deptno;
	}
	public String getDname() {
		return dname;
	}
	public void setDname(String dname) {
		this.dname = dname;
	}
	public String getLoc() {
		return loc;
	}
	public void setLoc(String loc) {
		this.loc = loc;
	}
	public List<Emp> getEmps() {
		return emps;
	}
	public void setEmps(List<Emp> emps) {
		this.emps = emps;
	}
	@Override
	public String toString() {
		return "Dept [deptno=" + deptno + ", " + (dname != null ? "dname=" + dname + ", " : "")
				+ (loc != null ? "loc=" + loc + ", " : "") + (emps != null ? "emps=" + emps : "") + "]";
	}
	
}

package pojo;

public class Emp {
	private int empno;
	private String ename;
	private String job;
	private int mgr;
	private String hiredate;
	private double sal;
	private double comm;
	private int deptno;
	private Dept dept;
	public int getEmpno() {
		return empno;
	}
	public void setEmpno(int empno) {
		this.empno = empno;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public int getMgr() {
		return mgr;
	}
	public void setMgr(int mgr) {
		this.mgr = mgr;
	}
	public String getHiredate() {
		return hiredate;
	}
	public void setHiredate(String hiredate) {
		this.hiredate = hiredate;
	}
	public double getSal() {
		return sal;
	}
	public void setSal(double sal) {
		this.sal = sal;
	}
	public double getComm() {
		return comm;
	}
	public void setComm(double comm) {
		this.comm = comm;
	}
	public int getDeptno() {
		return deptno;
	}
	public void setDeptno(int deptno) {
		this.deptno = deptno;
	}
	public Dept getDept() {
		return dept;
	}
	public void setDept(Dept dept) {
		this.dept = dept;
	}
	@Override
	public String toString() {
		return "Emp [empno=" + empno + ", " + (ename != null ? "ename=" + ename + ", " : "")
				+ (job != null ? "job=" + job + ", " : "") + "mgr=" + mgr + ", "
				+ (hiredate != null ? "hiredate=" + hiredate + ", " : "") + "sal=" + sal + ", comm=" + comm
				+ ", deptno=" + deptno + ", " + (dept != null ? "dept=" + dept : "") + "]";
	}
}

全域性配置檔案:mybatis.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>
	<!-- 載入Java屬性檔案 -->
	<properties resource="jdbc.properties"></properties>
	<typeAliases>
		<package name="pojo"/>
	</typeAliases>
	<!-- 配置環境 -->
	<environments default="dev">
		<environment id="dev">
			<transactionManager type="JDBC"></transactionManager>
			<dataSource type="POOLED">
				<!-- name屬性的值都是固定的,必須這麼寫,否則框架無法識別 -->
				<property name="driver" value="${driver}"/>
				<property name="url" value="${url}"/>
				<property name="username" value="${username}"/>
				<property name="password" value="${password}"/>
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<!-- 批量註冊對映檔案:mappr介面和對映檔案必須同包同名 -->
		<package name="mapper"/>
	</mappers>
</configuration>

mappr:介面+對映檔案

package mapper;
import java.util.List;
import pojo.Dept;
public interface DeptMapper {
	List<Dept> findDeptAndEmps();
}

package mapper;
import java.util.List;
import pojo.Emp;
public interface EmpMapper {
	List<Emp> findEmpAndDept();
}
<?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="mapper.DeptMapper">
	<!-- id:唯一標識    type:封裝型別 -->
	<resultMap type="Dept" id="deptMap">
		<!--主鍵欄位的對映    property:屬性名稱    column:結果集中欄位名稱    
			property的值必須與實體類中的屬性名稱一致,否則找不到setter訪問器 -->
		<id property="deptno" column="deptno" />
		<result property="dname" column="dname" />
		<result property="loc" column="loc" />
		<!-- 將表中的資料封裝到集合中  property:Dept類的屬性  
			column:dept表和emp表的關聯欄位    ofType:dept類的屬性emps集合中資料的型別 -->
		<collection property="emps" ofType="Emp" column="deptno">
			<id property="empno" column="empno" />
			<result property="ename" column="ename" />
			<result property="job" column="job" />
			<result property="mgr" column="mgr" />
			<result property="hiredate" column="hiredate" />
			<result property="sal" column="sal" />
			<result property="comm" column="comm" />
			<result property="deptno" column="deptno" />
		</collection>
	</resultMap>
	<!-- resultMap的值是上面resultMap標籤的唯一標識id -->
	<select id="findDeptAndEmps" resultMap="deptMap">
		select * from dept left join emp on dept.deptno=emp.deptno
	</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="mapper.EmpMapper">
	<resultMap type="emp" id="empMap">
		<id property="empno" column="empno"/>
		<result property="ename" column="ename" />
		<result property="job" column="job" />
		<result property="mgr" column="mgr" />
		<result property="hiredate" column="hiredate" />
		<result property="sal" column="sal" />
		<result property="comm" column="comm" />
		<result property="deptno" column="deptno" />
		<!-- 將結果集封裝到物件中     property:Emp類的屬性  
			column:emp表和dept表的關聯欄位  javaType:Emp類的屬性的資料型別-->
		<association property="dept" column="deptno" javaType="dept">
			<id property="deptno" column="deptno" />
			<result property="dname" column="dname"/>
			<result property="loc" column="loc"/>
		</association>
	</resultMap>
	<select id="findEmpAndDept" resultMap="empMap">
		select * from emp left join dept on dept.deptno=emp.deptno
	</select>
</mapper>

測試類:Test

package test;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import mapper.DeptMapper;
import mapper.EmpMapper;

public class Test {
	public static void main(String[] args) throws IOException {
		Reader reader = Resources.getResourceAsReader("mybatis.xml");
		SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		SqlSession session = ssf.openSession();
		
		DeptMapper mapper = session.getMapper(DeptMapper.class);
		System.out.println(mapper.findDeptAndEmps());
		
		EmpMapper mapper1 = session.getMapper(EmpMapper.class);
		System.out.println(mapper1.findEmpAndDept());
		session.close();
	}
}