MyBatis介面代理方式實現Dao層
目錄
MyBatis介面代理方式實現Dao層
區別
1、selectlist和getMapper區別
//4.執行對映配置檔案中的sql語句,並接收結果
list = sqlSession.selectList("StudentMapper.selectAll");
//4.獲取StudentMapper介面的實現類物件 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); //5.通過實現類物件呼叫方法,接收結果 result = mapper.delete(id);
兩者其實並無區別,只不過StudentMapper利用代理物件操作sqlSession.selectList();
2、namespace的作用
<mapper namespace="StudentMapper">
<mapper namespace="com.zhu.mapper.StudentMapper">
1.完全限定名(比如“com.mypackage.MyMapper.selectAllThings”)將被直接查詢並且找到即用。
2.短名稱(比如“selectAllThings”)如果全域性唯一也可以作為一個單獨的引用。如果不唯一,有兩個或兩個以上的相同名稱(比如“com.foo.selectAllThings ”和“com.bar.selectAllThings”),那麼使用時就會收到錯誤報告說短名稱是不唯一的,這種情況下就必須使用完全限定名。
Mybatis中namespace用於繫結dao介面,dao介面的方法對應mapper中的sql語名
介面代理方式 - 實現規則
1)對映配置檔案中的名稱空間必須和Dao層介面的全類名相同
2)對映配置檔案中的增刪改查的標籤id屬性必須和Dao層介面層的方法名相同
3)對映配置檔案中的增刪改查標籤的parameterType屬性必須和Dao層介面方法的引數相同
4)對映配置檔案中的增刪改查標籤的resultType屬性必須和Dao層介面方法的返回值相同
介面代理方式 - 程式碼實現
1)刪除mapper層介面的實現類
2)修改對映配置檔案
3)修改service層介面的實現類,採用介面代理方式實現功能
在寫StudentMapper.java時
注意
1、
resultType="student"
方法的返回值為List<Student>
2、
parameterType="student"
方法的引數為Student student
3、如何取出引數中的值 #{}
· MyBatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD約束-->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<settings>
<setting name="logImpl" value="log4j"/>
</settings>
<!--預設名為類名首字母小寫-->
<typeAliases>
<package name="com.zhu.bean"/>
</typeAliases>
<environments default="mysql">
<!--environment配置資料庫環境 id 屬性唯一標識 -->
<environment id="mysql">
<!-- transactionManager事務管理。type屬性,採用JDBC預設的事務-->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource資料來源資訊 type屬性 連線池 MyBatis預設有三種資料來源-->
<dataSource type="POOLED">
<!--property獲取資料庫連線的配置資訊-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- mappers引入對映配置檔案 -->
<mappers>
<!-- mapper 引入指定的對映配置檔案 resource屬性指定對映配置檔案的名稱 -->
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
· StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD約束-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper: 核心根標籤
namespace屬性: 名稱空間
-->
<mapper namespace="com.zhu.mapper.StudentMapper">
<select id="selectAll" resultType="student">
SELECT * FROM student
</select>
<insert id="insert" parameterType="student">
INSERT INTO student VALUES (#{id},#{name},#{age});
</insert>
<delete id="delete" parameterType="int">
DELETE FROM student WHERE id=#{id};
</delete>
</mapper>
· controller - studentController.java
public class StudentController {
private StudentService studentService = new StudentServiceImpl();
@Test
public void selectAll(){
List<Student> list = studentService.selectAll();
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void insert(){
Student st = new Student(10,"路飛",23);
Integer insert = studentService.insert(st);
System.out.println(insert);
}
@Test
public void delete(){
Integer s = 3;
Integer i = studentService.delete(3);
System.out.println(i);
}
}
· mapper - StudentMapper.java
public interface StudentMapper{
/*
注意: resultType 值為 student 指定結果對映的物件型別
方法的返回值為 List<Student>
resultType:
1、基本型別 :resultType=基本型別
2、List型別 :resultType=List中元素的型別
3、Map型別 單條記錄:resultType =map
多條記錄:resultType =Map中value的型別
方法的返回值可以為List<Student>
parameterType:
基本資料型別:int,string,long,Date;
複雜資料型別:類和Map
如何獲取引數中的值:
基本資料型別:#{value}或${value} 獲取引數中的值
複雜資料型別:#{屬性名}或${屬性名} ,map中則是#{key}或${key}
*/
public abstract List<Student> selectAll();
Integer insert(Student student);
Integer delete(Integer integer);
}
· service - StduentService.java - StudentServiceImpl.java
public interface StudentService {
List<Student> selectAll();
Integer insert(Student student);
Integer delete(Integer integer);
}
public class StudentServiceImpl implements StudentService {
@Override
public List<Student> selectAll() {
List<Student> list = null;
SqlSession sqlSession = null;
InputStream is = null;
try {
is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
//獲取StudentMapper介面的實現類物件
//獲取對應的Mapper,讓對映器通過名稱空間和方法名稱找到對應的SQL,傳送給資料庫執行後返回結果。
//操作資料庫主要是通過SQL語句,那麼只要找到SQL語句然後執行不就可以
//sqlSession.getMapper()的內部產生了StudentMapper的實現類,那怎麼產生的呢?
//動態代理
/*
被代理物件:真實的物件
代理物件:記憶體中的一個物件
*/
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//StudentMapper studentMapper1 = new StudentServiceImpl();
//return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
/*
類載入器:和被代理物件使用相同的類載入器
介面型別Class陣列:和被代理物件使用相同介面
代理規則:完成代理增強的功能
*/
//通過代理物件呼叫方法,接收結果
list = mapper.selectAll();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(sqlSession != null) {
sqlSession.close();
}
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return list;
}
介面代理方式 - 問題
分析動態代理物件如何生成的?
通過動態代理開發模式,我們只編寫一個介面,不寫實現類,我們通過getMapper() 方法最終獲取到 org.apache.ibatis.binding.MapperProxy代理物件,然後執行功能,而這個代理物件正是 MyBatis使用了 JDK 的動態代理技術,幫助我們生成了代理實現類物件。從而可以進行相關持久化操作。
分析方法是如何執行的?
動態代理實現類物件在執行方法的時候最終呼叫了mapperMethod.execute() 方法,這個方法中通過 switch 語句根據操作型別來判斷是新增、修改、刪除、查詢操作,最後一步回到了MyBatis 最原生的 SqlSession方式來執行增刪改查。
小結
-
原生:Dao ---> DaoImpl
-
介面:Mapper ---> xxMapper.xml
-
介面代理方式可以讓給我們只編寫介面即可,而實現類物件由MyBatis生成
-
獲取動態代理物件
SqlSession功能類中的getMapper(Mapper介面.class)方法。
MyBatis對映配置檔案 - 動態SQL
"if標籤" +
<select id="selectCondition" resultType="student" parameterType="student">
SELECT * FROM student
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
"foreach"
foreach 用來迭代使用者傳過來的List或者Array
如:使用foreach元素來構建in子語句
collection
: 指定要遍歷的集合名稱 list、array
item
: 用來臨時存放迭代集合中當前元素的值,便於在foreach中使用
open
:將該屬性指定的值新增到foreach迭代後拼出字串的開始
close
:將該屬性指定的值新增到foreach迭代拼出字串的結尾
separator
: 用來分割foreach元素迭代的每個元素
<!--SELECT * FROM student WHERE id IN (1,2);
-->
<select id="selectByIds" resultType="student" parameterType="list">
SELECT * from student
<where>
<foreach collection="list" open="id IN(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
<select id="selectByIds2" resultType="student" parameterType="list">
SELECT * from student where id IN
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
<!--如果是 selectById(List<Student> stus) 這種-->
<select id="selectByIds3" resultType="student" parameterType="list">
SELECT * from student
<where>
<foreach collection="list" open="id IN(" close=")" item="stu" separator=",">
#{stu.id}
</foreach>
</where>
</select>
使用
StudentMapper.java
List<Student> selectByIds(List<Integer> list);
StudentService.java
List<Student> selectByIds(List<Integer> list);
sql片段抽取
<sql id="select">SELECT * FROM student</sql>
使用
<include refid="select"/>
小結
- 動態 SQL 指的就是 SQL 語句可以根據條件或者引數的不同進行動態的變化。
:條件標籤。 :條件判斷的標籤。 :迴圈遍歷的標籤。 :抽取SQL 片段的標籤。 :引入SQL 片段的標籤。
MyBatis 核心配置檔案 - 分頁查詢
使用PageHelper
功能實現
(1) 匯入jar包 分頁外掛pagehelper-5.1.10.jar 和依賴的包 jsqlparser-3.1.jar
(2) 在核心配置檔案中整合分頁助手外掛
(3) 在測試類中使用分頁助手相關API實現分頁功能。
MyBatisConfig.xml
<!-- 環境配置順序
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用
StudentServiceImpl.java
PageHelper.startPage(1,3); 一定要寫在SQL執行之前
//通過分頁助手來實現分頁功能
// 第一頁:顯示3條資料
//PageHelper.startPage(1,3);
// 第二頁:顯示3條資料
//PageHelper.startPage(2,3);
// 第三頁:顯示3條資料
PageHelper.startPage(3,3);
//5.呼叫實現類的方法,接收結果
List<Student> list = mapper.selectAll();PageHelper.startPage(0,1);
相關API
返回值 | 方法名 | 說明 |
---|---|---|
long | getTotal() | 獲取總條數 |
int | getPages() | 獲取總頁數 |
int | getPageNum() | 獲取當前頁 |
int | getPageSize() | 獲取每頁顯示條數 |
int | getPrePage() | 獲取上一頁 |
int | getNextPage() | 獲取下一頁 |
boolean | isIsFirstPage() | 獲取是否是第一頁 |
boolean | isIsLastPage() | 獲取是否是最後一頁 |
MyBatis 多表操作
外來鍵欄位的建立是體現表與表關係的所在
- 一對一 : 在任意一方建立外來鍵,關聯對方的主鍵
使用者基本資訊表 和 使用者詳細資訊表
- 一對多 : 在多的一方建立外來鍵,關聯一的一方主鍵
店鋪表 和 商品表
使用者表 和 訂單表
- 多對多 : 藉助中間表,中間表至少兩個欄位,分別關聯兩張表的主鍵
學生表 和 課程表
使用者表 和 興趣愛好表
多表操作 一對一
模型 : 人和身份證 、 一個人只有一張身份證
資料準備
CREATE DATABASE db2;
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT
);
INSERT INTO person VALUES (NULL,'張三',23);
INSERT INTO person VALUES (NULL,'李四',24);
INSERT INTO person VALUES (NULL,'王五',25);
CREATE TABLE card(
id INT PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(30),
pid INT,
CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
)
INSERT INTO card VALUES (NULL,'12345',1);
INSERT INTO card VALUES (NULL,'23456',2);
INSERT INTO card VALUES (NULL,'34567',3);
bean
Student.java
public class Person {
private Integer id; //主鍵id
private String name; //人的姓名
private Integer age; //人的年齡
}
Card.java
public class Card {
private Integer id;
private String number;
private Person p;
}
OneToOneMapper.xml
<mapper namespace="com.mybatis.oneToOne.OneToOneMapper">
<!--
<resultMap>是Mybatis的結果集封裝
-->
<!--通過 <resultMap>: 配置欄位和物件屬性的對映關係標籤
屬性
id 屬性:resultMap 的唯一標識,此 id 值用於 select 元素 resultMap 屬性的引用。
type 屬性:表示該 resultMap 的對映結果型別(通常是 Java 實體類)。
子節點
id 子節點:一般對應資料庫中該行的主鍵 id,設定此項可以提升 MyBatis 效能。
配置主鍵對映關係標籤
result 子節點:對映到 JavaBean 的某個 “簡單型別” 屬性,如基礎資料型別、包裝類等。
配置非主鍵對映關係標籤
子節點屬性
column 屬性:表示從資料庫中查詢的欄位名或別名。表中欄位名稱
property 屬性:表示查詢出來的欄位對應的值賦給實體物件的哪個屬性。實體物件變數名稱
說明:子節點 id 和 result 均可實現最基本的結果集對映,將列對映到簡單資料型別的屬性。
這兩者唯一不同的是:在比較物件例項時 id 將作為結果集的標識屬性。
這有助於提高總體效能,特別是應用快取和巢狀結果對映的時候。
而若要實現高階結果對映,就需要學習下面兩個配置項: association 和 collection。
association:對映到 JavaBean 的某個 “複雜型別” 屬性,比如 JavaBean 類,
即 JavaBean 內部巢狀一個複雜資料型別(JavaBean)屬性,這種情況就屬於複雜型別的關聯。
但是需要注意: association 僅處理一對一的關聯關係。
association:配置被包含物件的對映關係
property:被包含物件的變數名
javaType:被包含物件的資料型別
-->
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result column="number" property="number"/>
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<select id="selectAll" resultMap="oneToOne">
SELECT
c.id cid,
number,
pid,
NAME,
age
FROM
card c , person p
WHERE
c.pid = p.id
</select>
</mapper>
MyBatisConfig.xml
<mappers>
<!-- mapper 引入指定的對映配置檔案 resource屬性指定對映配置檔案的名稱 -->
<mapper resource="com/mybatis/oneToOne/OneToOneMapper.xml"></mapper>
</mappers>
測試
//4.獲取OneToOneMapper介面的實現類物件
OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
//5.呼叫實現類的方法,接收結果
List<Card> list = mapper.selectAll();
//6.處理結果
for (Card c : list) {
System.out.println(c);
}
多表操作 一對多
資料準備
多表操作 一對多 班級對學生
CREATE TABLE classes(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARBINARY(20)
);
INSERT INTO classes VALUES (NULL,'s一班');
INSERT INTO classes VALUES (NULL,'s二班');
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
cid INT,
CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,'張三',23,1);
INSERT INTO student VALUES (NULL,'李四',24,1);
INSERT INTO student VALUES (NULL,'王五',25,2);
INSERT INTO student VALUES (NULL,'趙六',26,2);
bean
public class Student {
private Integer id;
private String name;
private Integer age;
}
public class Classes {
private Integer id;
private String name;
private List<Student> students; //班級中所有學生物件
}
OneToManyMapper.xml
<mapper namespace="com.mybatis.oneToMany.OneToManyMapper">
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!--
collection:配置被包含的集合物件對映關係
property:被包含物件的變數名
ofType:被包含物件的實際資料型別
-->
<collection property="students" ofType="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="oneToMany">
SELECT
c.id cid,
c.name cname,
s.id sid,
s.name sname,
s.age sage
FROM
classes c,student s
WHERE
c.id=s.cid
</select>
測試
//4.獲取OneToManyMapper介面的實現類物件
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
//5.呼叫實現類的方法,接收結果
List<Classes> classes = mapper.selectAll();
//6.處理結果
for (Classes cls : classes) {
System.out.println(cls.getId() + "," + cls.getName());
List<Student> students = cls.getStudents();
for (Student student : students) {
System.out.println("\t" + student);
}
}
多表操作 多對多
資料準備
學生和課程 一個學生可以選擇多門課程 一個課程可以被多個學生選擇
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO course VALUES (NULL,'語文');
INSERT INTO course VALUES (NULL,'數學');
CREATE TABLE stu_cr(
id INT PRIMARY KEY AUTO_INCREMENT,
sid INT,
cid INT,
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);
INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);
bean
Course.java
public class Course {
private Integer id;
private String name;
}
Student.java
public class Student {
private Integer id;
private String name;
private Integer age;
private List<Course> courses; //學生所選的課程集合
}
ManyToManyMapper.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">
<mapper namespace="com.mybatis.manyToMany.ManyToManyMapper">
<resultMap id="ManyToMany" type="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<!-- <collection>:配置被包含集合物件的對映關係標籤。
屬性:
property 屬性:被包含集合物件的變數名
ofType 屬性:集合中儲存的物件資料型別-->
<collection property="courses" ofType="course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="ManyToMany">
SELECT
sc.sid,
s.name sname,
s.age sage,
sc.cid,
c.name cname
FROM
student s,course c,stu_cr sc
WHERE
sc.sid=s.id AND sc.cid=c.id
</select>
</mapper>
處理結果
//5.呼叫實現類的方法,接收結果
List<Student> students = mapper.selectAll();
//6.處理結果
for (Student student : students) {
System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println("\t" + cours);
}
}
小結
<!--一對一 所有標籤如下
<resultMap>:配置欄位和物件屬性的對映關係標籤。
屬性:
id 屬性:唯一標識
type 屬性:實體物件型別
<id>:配置主鍵對映關係標籤。
<result>:配置非主鍵對映關係標籤。
屬性:
column 屬性:表中欄位名稱
property 屬性: 實體物件變數名稱
(*)<association>:配置被包含物件的對映關係標籤。 屬性:
property 屬性:被包含物件的變數名
javaType 屬性:被包含物件的資料型別
-->
<!--多對多 & 一對多 所有標籤如下
<resultMap>:配置欄位和物件屬性的對映關係標籤。
屬性:
id 屬性:唯一標識
type 屬性:實體物件型別
<id>:配置主鍵對映關係標籤。
<result>:配置非主鍵對映關係標籤。
屬性:
column 屬性:表中欄位名稱
property 屬性: 實體物件變數名稱
(*)<collection>:配置被包含集合物件的對映關係標籤。
屬性:
property 屬性:被包含集合物件的變數名
ofType 屬性:集合中儲存的物件資料型別
-->
擴充套件
針對結果集而建立對應的javabean類,來接收結果集資料,這種javabean在領域驅動模型中稱之為 : VO