MyBatis學習筆記(一), 內容包括:html
學習視頻:尚硅谷雷豐陽老師MyBatis
https://www.bilibili.com/video/BV1bb411A7bDjava
鏈接數據庫的方法:mysql
工具:JDBC→Dbutils(QueryRunner)→JdbcTemplategit
框架:總體解決方案github
Hibernate:全自動全映射ORM(Object Relation Mapping)框架,旨在消除SQLsql
缺點:數據庫
SQL和Java編碼分開,功能邊界清晰,一個專一業務、一個專一數據。數組
MyBatis:半自動,輕量級的框架,sql語句提取到配置文件中編寫安全
Maven 依賴:網絡
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>
官方文檔:https://mybatis.org/mybatis-3...
核心步驟:
建立tbl_employee表並插入三條數據:
CREATE TABLE tbl_employee( id INT(10) PRIMARY KEY AUTO_INCREMENT, last_name VARCHAR(255), gender CHAR(1), email VARCHAR(255) )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO tbl_employee VALUES (1,"zhansgan",0,"zhangsan@qq.com"), (2,"lisi",0,"lisi@163.com"), (3,"wangwu",1,"wangwu@126.com");
<dependencies> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <!--log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
public class Employee { private Integer id; private String lastName; //注意,該名稱與數據庫表的字段名不一致,查詢會出現問題,能夠用別名解決 private String email; private String gender; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Employee{" + "id=" + id + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", gender='" + gender + '\'' + '}'; } }
<?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"> <!--mybatis核心配置文件--> <configuration> <!--配置環境組,選擇默認的環境id--> <environments default="development"> <!--配置單個環境並指定id爲development--> <!--能夠同時配置多個環境,可是隻能選擇一個使用--> <environment id="development"> <!--配置事務管理類型,JDBC--> <transactionManager type="JDBC"/> <!--配置鏈接池POOLED--> <dataSource type="POOLED"> <!--數據庫鏈接池4個參數--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--配置映射,註冊Mapper文件--> <mappers> <mapper resource="com/xiao/dao/EmployeeMapper.xml"/> </mappers> </configuration>
注意:
在xml配置文件中,url中的 &符號須要寫成 &
在resources目錄下表編寫sql映射文件:EmployeeMapper.xml
sql語句的參數用#獲取
<?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:名稱空間,能夠綁定一個Mapper接口--> <!--編寫sql語句--> <!--id:惟一標識,若是有Mapper接口文件,須要對應其中的方法名--> <!--resultType:對應返回結果類型--> <!--#{id}:從傳遞過來的參數中取出id值--> <mapper namespace="com.xiao.dao.EmployeeMapper"> <select id="selectEmp" resultType="com.xiao.pojo.Employee"> select * from tbl_employee where id = #{id} </select> </mapper>
根據全局配置文件建立SqlSessionFactory對象,SqlSessionFactoryBulid→SqlSessionFactory→SqlSession。由sqlSession對象實例執行sql語句。
public class MyBatisTest { @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //由SqlSessionFactoryBuilder對象獲取SqlSessionFactory對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //由SqlSession工廠得到SqlSession對象,使用其進行增刪改查 SqlSession sqlSession = sqlSessionFactory.openSession(); try { //sqlSession,直接執行已經映射的sql語句 //selectOne()中兩個參數:sql的惟一標識(對應sql映射文件中的namespace.id)和執行sql須要的參數 Employee employee = sqlSession.selectOne("com.xiao.dao.EmployeeMapper.selectEmp", 1); System.out.println(employee); } finally { //一個sqlSession就是和數據庫的一次會話,使用完以後須要關閉資源 sqlSession.close(); } } }
查詢結果:
Employee{id=1, lastName='null', email='zhangsan@qq.com', gender='0'}
因爲實體類的成員變量名和字段名不一致,所以lastName查詢結果爲null,能夠在sql語句中取別名解決,也能夠在sql映射文件中配置,後面會講到。
修改sql語句:
select id,last_name lastName,gender,email from tbl_employee where id = #{id}
查詢結果:
Employee{id=1, lastName='zhansgan', email='zhangsan@qq.com', gender='0'}
一種更有效的方法:建立接口文件com.xiao.dao.EmployeeMapper與sql映射文件綁定,在接口文件中定義方法,能夠聲明操做的返回值和參數。
注意:
public interface EmployeeMapper { //定義一個查詢方法 Employee selectEmployeeById(Integer id); }
測試文件:
try { //得到接口文件的代理對象,執行方法 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); System.out.println(mapper.selectEmployeeById(1)); } finally { sqlSession.close(); }
build(InputStream in)
openSession()
,傳入參數true能夠設置爲自動提交事務。getMapper()
,須要傳入dao接口的class類型參數UserDao.class能夠理解爲數據庫鏈接池對象,一旦建立了 SqlSessionFactory,就再也不須要它了,所以最佳做用域是方法做用域(也就是局部方法變量);
能夠理解爲數據庫鏈接對象,一旦被建立就應該在應用的運行期間一直存在,所以 SqlSessionFactory 的最佳做用域是應用做用域。使用單例模式或者靜態單例模式。
鏈接到鏈接池的一個請求,每一個線程都有它本身的 SqlSession 實例。SqlSession 的實例不是線程安全的,所以是不能被共享的,因此它的最佳的做用域是請求或方法做用域。
爲了確保每次都能執行關閉操做,把關閉操做放到 finally 塊中。
約束文件:
<?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">
MyBatis 的配置文件包含了會深深影響 MyBatis 行爲的設置和屬性信息。 配置文檔的頂層結構以下,必須按順序配置
configuration(配置)
environments(環境配置)
environment(環境變量)
經過properties屬性能夠實現引用配置文件,這些屬性都是能夠外部配置且可動態替換的。
<properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </properties> .... <!--使用${name}引用相應的值--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> ....
也能夠引入外部的配置文件,例如編寫一個外部的配置文件db.properties,而後在覈心配置文件中引入:
#外部配置文件 driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username=root password=root
核心配置文件:
<!--在覈心配置文件中引入外部配置文件,而後就可使用${name}引用--> <properties resource="db.properties"> </properties>
【注意事項】:若是一個屬性在不僅一個地方進行了配置,那麼,MyBatis 將按照下面的順序來加載:
所以,經過方法參數傳遞的屬性具備最高優先級,resource/url 屬性中指定的配置文件次之,最低優先級的則是 properties 元素中指定的屬性。
是 MyBatis 中極爲重要的調整設置,它們會改變 MyBatis 的運行時行爲。
經常使用設置:
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
fetchType
屬性來覆蓋該項的開關狀態。默認是false類型別名可爲 Java 類型設置一個縮寫名字。 僅用於 XML 配置,意在下降冗餘的全限定類名書寫。別名不區分大小寫
單個類起別名,<typeAlias>
標籤:
<typeAliases> <!-- type:指定要起別名的類型全類名,默認是類名小寫employee--> <!-- alias:指定新的別名--> <typeAlias type="com.xiao.pojo.Employee"/> </typeAliases>
<package>
標籤,<typeAliases> <package name="com.xiao.pojo"/> </typeAliases>
@Alias
,則別名爲其註解值:@Alias("Emp") public class Employee { ... }
常見的 Java 類型內建的類型別名。它們都是不區分大小寫的,自定義的別名不要與其重複。
基本類型前面加下劃線,引用類型首字母小寫:
別名 | 映射的類型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
數據庫裏的字段類型與Java的數據類型進行映射。
在設置預處理語句(PreparedStatement)中的參數或從結果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉換成 Java 類型。
MyBatis 容許在映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括:
MyBatis 能夠配置成適應多種環境,能夠配置多個環境,但每一個 SqlSessionFactory 實例只能選擇一種環境。default參數指定使用某種環境。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
【注意事項】:
MyBatis 能夠根據不一樣的數據庫廠商執行不一樣的語句,考慮了移植性。
獲得數據庫廠商的標識(驅動getDatabaseProductName()),MyBatis就能根據數據庫廠商標識來執行不一樣的sql。
<databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider>
須要告訴 MyBatis 到哪裏去找到 sql語句。可使用相對於類路徑的資源引用,或徹底限定資源定位符(包括 file:///
形式的 URL),或類名和包名等。
<mappers> <!-- 使用相對於類路徑的資源引用 --> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <!-- 使用徹底限定資源定位符(URL) --> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <!-- 使用映射器接口實現類的徹底限定類名 --> <mapper class="org.mybatis.builder.AuthorMapper"/> <!-- 將包內的映射器接口實現所有註冊爲映射器 --> <package name="org.mybatis.builder"/> </mappers>
在dao接口文件中,用註解實現查詢:
public interface EmployeeMapper { //定義一個查詢方法 @Select("select * from tbl_employee where id = #{id}") Employee selectEmployeeById(Integer id); }
【一個小坑】:
映射文件指導MyBatis如何進行數據庫增刪改。
1) 首先在接口文件com.xiao.dao.EmployeeMapper中聲明對應的增刪改查方法,
同時能夠設置返回值類型Integer、Long、Boolean,表示被影響的行數
public interface EmployeeMapper { //定義一個查詢方法 @Select("select * from tbl_employee where id = #{id}") Employee selectEmployeeById(Integer id); //增長 void addEmp(Employee employee); //根據id刪除 void deleteEmpById(Integer id); //修改 void updateEmp(Employee employee); }
2) 而後在sql映射文件EmployeeMapper.xml中編寫相應的sql語句
<!--parameterType:參數類型,能夠省略--> <insert id="addEmp" parameterType="employee"> insert into tbl_employee (last_name, gender, email) value (#{lastName},#{gender},#{email}) </insert> <delete id="deleteEmpById"> delete from tbl_employee where id = #{id} </delete> <update id="updateEmp"> update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id = #{id} </update>
3) 測試:
public void test2() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); //默認不自動提交事務,須要手動提交,或者構造方法傳入true SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); //增 mapper.addEmp(new Employee(4,"林青霞","lingqingxia@163.com","1")); //刪 mapper.deleteEmpById(2); //改 mapper.updateEmp(new Employee(1,"張柏芝","zhangbozhi@qq.com","1")); // 手動提交事務 sqlSession.commit(); } finally { sqlSession.close(); } }
注意事項:增刪改須要提交事務
sqlSessionFactory.openSession():須要手動提交, sqlSession.commit();
sqlSessionFactory.openSession(true)是自動提交事務的
MySQL支持自增主鍵,在MyBatis中也是使用statement.getGeneratedKeys()獲取自增主鍵的值。
用法,insert標籤中:
<!--useGeneratedKeys="true":使用自增主鍵獲取主鍵值策略--> <!--keyProperty="id":獲取到的主鍵封裝給JavaBean的id屬性--> <insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id"> insert into tbl_employee (last_name, gender, email) value (#{lastName},#{gender},#{email}) </insert>
測試:employee.getId()方法能夠獲取到自增的主鍵值
Employee employee = new Employee(null,"Tom","tom@126.com","0"); mapper.addEmp(employee); System.out.println(employee.getId());
取參數的方式:#{參數名}
MyBatis不會作處理,傳入id,sql語句中寫 #{idabc} 也能取到參數
會被封裝成一個map,#{}就是從map中獲取指定key值的value。
key:param1,...paramN,或者參數的索引也能夠
value:傳入的參數值
若是按照以前的寫法會報異常:
Employee employee = mapper.selectEmpByIdAndName(6, "Tom"); <select id="selectEmpByIdAndName" resultType="employee"> select * from tbl_employee where id = #{id} and last_name=#{lastName} </select>
BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
正確寫法,可是通常不這麼用:
select * from tbl_employee where id = #{param1} and last_name=#{param2}
常規用法:在接口方法中加註解@Param
:至關於key中保存的是@Param註解指定的值
Employee selectEmpByIdAndName(@Param("id") Integer id, @Param("lastName") String lastName);
//封裝傳入參數爲Map,查詢 Employee selectEmpByMap(Map<String,Object> map);
HashMap<String,Object> map = new HashMap<>(); //注意sql語句中#{id},#{lastName}與map中的鍵字段名稱一一對應 map.put("id",6); map.put("lastName","Tom"); Employee employee = mapper.selectEmpByMap(map); System.out.println(employee);
select * from tbl_employee where id = #{id} and last_name=#{lastName}
Employee getEmp(@Param("id") Integer id, String lastName)
取值:id ==> #{id}或者#{param1},lastName ==> #{param2}
Employee getEmp(Integer id, @Param("e") Employee emp)
取值:id ==> #{param1},lastName ==> #{e.lastName}或者 #{param2.lastName}
key:collection,list,array
Employee getEmp(List<Integer> ids)
取值:取出第一個id的值 #{list[0]}
總結:參數多時封裝成map,結合@Param指定封裝時使用的key,#{key}取出map中的值
#{}
:是以預編譯的形式,即佔位符,將參數設置到sql語句中,相似於JDBC中的PreparedStament,防止sql注入
${}
:取出的值直接拼裝在sql語句中,會有安全問題。
原生JDBC中不支持佔位符的地方就能夠用${取值},好比表名、排序字段等
select * from ${year}_salary where ...
select * from tbl_employee order by ${name}
select語句中resultType寫的是集合中元素的類型
//查詢全部,返回一個集合 List<Employee> selectAll();
<select id="selectAll" resultType="employee"> select * from tbl_employee </select>
測試:
List<Employee> employees = mapper.selectAll(); for (Employee employee : employees) { System.out.println(employee); }
結果:
Employee{id=1, lastName='張曼玉', email='zhangmanyu@163.com', gender='1'} Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'} Employee{id=5, lastName='林青霞', email='lingqingxia@163.com', gender='1'} Employee{id=6, lastName='Tom', email='tom@126.com', gender='0'}
若是想把一條記錄的返回值類型封裝爲map,key值爲字段名,value值。則指定resultType爲map,查詢到的結果會將指定的列映射到Map 的鍵上,結果映射到值上。
//返回一條記錄的map,key是列名,值就是對應的值 Map<String,Object> selectEmpByIdToMap(Integer id);
<!--查詢並返回一條記錄的map--> <select id="selectEmpByIdToMap" resultType="map"> select * from tbl_employee where id = #{id} </select>
測試:
Map<String, Object> map = mapper.selectEmpByIdToMap(1); System.out.println(map);
結果:
{gender=1, last_name=張曼玉, id=1, email=zhangmanyu@163.com}
若是想封裝多條記錄到map中,key是主鍵值,value是JavaBean對象,則在接口方法上使用@MapKey
註解指定key的屬性; resultType依然爲map中的value類型
//返回多條記錄封裝到map中,key是主鍵值,value是JavaBean對象 //@MapKey;指定返回的map的key @MapKey("id") Map<Integer,Employee> selectAllToMap();
<!-- 返回多條記錄封裝到map中,key是主鍵值,value是JavaBean對象--> <select id="selectAllToMap" resultType="employee"> select * from tbl_employee </select>
測試:
Map<Integer, Employee> map = mapper.selectAllToMap(); System.out.println(map);
結果:
{1=Employee{id=1, lastName='張曼玉', email='zhangmanyu@163.com', gender='1'}, 3=Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'}, 5=Employee{id=5, lastName='林青霞', email='lingqingxia@163.com', gender='1'}, 6=Employee{id=6, lastName='Tom', email='tom@126.com', gender='0'}}
而指定 resultType
屬性爲實體類時,查詢到的結果會將指定的列映射到類的成員變量名上,結果映射到成員變量值上。
【注意】:實體類中的屬性名和數據庫中的字段名保持一致。不然結果值會爲null。
若是列名和屬性名不能匹配上,且不知足駝峯命名自動映射,能夠在 SELECT 語句中設置列別名,也能夠顯式配置 ResultMap
若是列名和屬性名不能匹配上,在xml映射文件中顯式配置 ResultMap
標籤
<!--自定義結果集規則--> <!--type:自定義規則的Java類型--> <!--id:惟一id,用於引用--> <resultMap id="MyEmp" type="employee"> <!--用id標籤訂義主鍵底層會有優化,普通列用result標籤--> <!--column:指定哪一列--> <!--property:指定對應的JavaBean屬性--> <!--不指定的列會自動封裝--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </resultMap> <!--用resultMap取代resultType,值爲自命名的id--> <select id="selectEmp" resultMap="MyEmp"> select * from tbl_employee where id = #{id} </select>
處理多表查詢。
MyBatis 有兩種不一樣的方式加載關聯:
兩張表,員工表tbl_employee和部門表tbl_dept,員工表中設置外鍵部門id,指向部門表的主鍵
在以前的基礎上新建了部門表,並在員工表中設置外鍵,插入數據:
CREATE TABLE tbl_dept( id INT(10) PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(255) )ENGINE=INNODB DEFAULT CHARSET=UTF8; ALTER TABLE tbl_employee ADD COLUMN d_id INT(10); ALTER TABLE tbl_employee ADD CONSTRAINT fk_emp_dept FOREIGN KEY(d_id) REFERENCES tbl_dept(id);
員工表:
部門表:
Employee實體類:
public class Employee { private Integer id; private String lastName; private String email; private String gender; private Dept dept; //getter/setter... }
Dept實體類:
public class Dept { private Integer id; private String departmentName; //getter/setter... }
在接口文件中聲明一個根據員工id查詢員工信息的方法,由於員工信息中一個成員是部門對象,按照以前的映射關係,沒法查詢部門信息。
public interface EmployeeMapper { //根據id查詢員工的信息及其部門信息 Employee getEmpAndDept(Integer id); }
採用級聯屬性來封裝結果:
employee中有一個成員是dept,由dept.id和dept.departmentName則能夠獲取到部門的id和部門名稱
<!--聯合查詢,級聯屬性封裝結果集--> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="did" property="dept.id"/> <result column="dept_name" property="dept.departmentName"/> </resultMap> <select id="getEmpAndDept" resultMap="MyDifEmp "> <!--給列起別名--> select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = #{id} </select>
測試:
Employee employee = mapper.getEmpAndDept(1); System.out.println(employee);
結果:重點是執行的sql語句
DEBUG 06-12 20:16:45,106 ==> Preparing: select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:16:45,213 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:16:45,253 <== Total: 1 (BaseJdbcLogger.java:143) Employee{id=1, lastName='張曼玉', email='zhangmanyu@163.com', gender='1', dept=Dept{id=1, departmentName='開發部'}}
關聯(association)元素能夠處理多表查詢。
MyBatis 有兩種不一樣的方式加載關聯:
查詢結果本質上是Employee類,其中的成員Dept是對象,須要進行關聯association,用javaType獲取屬性的類型。兩種方式結果相同。
<!--查詢結果本質上是Employee類--> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <!--對查詢結果進行映射--> <!--數據庫中的字段取了別名後用別名--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <!-- dept成員是一個對象,須要用association指定聯合的JavaBean對象--> <!-- property="dept":指定哪一個屬性是聯合的對象--> <!-- javaType:指定這個屬性對象的類型,不能省略--> <association property="dept" javaType="com.xiao.pojo.Dept"> <id column="did" property="id"/> <id column="dept_name" property="departmentName"/> </association> </resultMap> <select id="getEmpAndDept" resultMap="MyDifEmp"> <!--給列起別名--> select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = #{id} </select>
思路:
<!--association分步查詢--> <!--第一步,先查詢員工信息--> <!--第二步,根據查詢到的員工信息的did,尋找對應的部門信息--> <select id="getEmpAndDept" resultMap="MyDifEmp"> <!--第一步,先查詢員工信息--> select id, last_name, gender,email,d_id from tbl_employee where id = #{id} </select> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <!--簡單的屬性直接寫--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <!--複雜的屬性,須要單獨處理--> <!--select:調用指定的方法查出結果--> <!--column:將哪一列的值傳給這個方法--> <!--property:查出的結果封裝給property指定的屬性--> <association property="dept" column="d_id" javaType="com.xiao.pojo.Dept" select="getDept"/> </resultMap> <!--第二步,根據查詢到的員工信息中的d_id,查詢對應的部門信息--> <!--能夠寫到部門的sql映射文件中,這裏簡略了--> <select id="getDept" resultType="com.xiao.pojo.Dept"> select id,dept_name departmentName from tbl_dept where id = #{d_id} </select>
association標籤中的屬性:
執行流程:使用select指定的方法,用傳入的column指定的這列參數的值,查出對象,並封裝給property屬性
結果:有兩條sql語句
DEBUG 06-12 20:04:49,831 ==> Preparing: select id, last_name, gender,email,d_id from tbl_employee where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:49,945 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,006 ====> Preparing: select id,dept_name departmentName from tbl_dept where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,008 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,015 <==== Total: 1 (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,017 <== Total: 1 (BaseJdbcLogger.java:143) Employee{id=1, lastName='張曼玉', email='zhangmanyu@163.com', gender='1', dept=Dept{id=1, departmentName='開發部'}}
延遲加載(懶加載、按需加載),須要在全局配置文件中開啓
fetchType
屬性來覆蓋該項的開關狀態。默認是false<settings> <!--開啓懶加載--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings>
測試中只使用查詢到的員工信息,不使用員工信息,則只會發出一條sql語句,若是不開啓延遲加載,則老是會發出兩條sql語句:
Employee employee = mapper.getEmpAndDept(1); System.out.println(employee.getEmail());
結果,只發出了第一步的sql語句:
DEBUG 06-12 20:31:55,182 ==> Preparing: select id, last_name, gender,email,d_id from tbl_employee where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:31:55,290 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:31:55,516 <== Total: 1 (BaseJdbcLogger.java:143) zhangmanyu@163.com
查詢部門,並講部門對應的全部員工信息查詢出來
public class Employee { private Integer id; private String lastName; private String email; private String gender; //getter/setter... }
Dept實體類:
public class Dept { private Integer id; private String departmentName; private List<Employee> emps; //getter/setter... }
DeptMapper接口文件:
public interface DeptMapper { Dept selectDeptById(Integer id); }
使用關聯查詢,根據Dept的id查詢Dept信息,其中Dept中有一個成員是emps,即員工的集合,須要用到collection標籤進行結果映射。
集合標籤:collection
sql映射文件:
<?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.xiao.dao.DeptMapper"> <!--嵌套結果查詢--> <select id="selectDeptById" resultMap="MyDept"> select d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name, e.email email, e.gender gender from tbl_dept d left join tbl_employee e on d.id = e.d_id where d.id = #{id} </select> <resultMap id="MyDept" type="com.xiao.pojo.Dept"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> <!--複雜的屬性,單獨處理,集合:collection,property指定哪一個屬性是集合,用ofType獲取集合中的泛型信息--> <collection property="emps" ofType="com.xiao.pojo.Employee"> <result column="eid" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap> </mapper>
測試:
Dept dept = mapper.selectDeptById(1); System.out.println(dept);
查詢結果,一個部門中有兩個員工信息:
DEBUG 06-12 21:24:02,595 ==> Preparing: select d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name, e.email email, e.gender gender from tbl_dept d left join tbl_employee e on d.id = e.d_id where d.id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 21:24:02,675 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 21:24:02,721 <== Total: 2 (BaseJdbcLogger.java:143) Dept{id=1, departmentName='開發部', emps=[Employee{id=1, lastName='張曼玉', email='zhangmanyu@163.com', gender='1'}, Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'}]}
思路與多對一中的思路類型:
<!--分步查詢--> <select id="selectDeptById" resultMap="MyDept"> select id, dept_name from tbl_dept where id = #{id} </select> <resultMap id="MyDept" type="com.xiao.pojo.Dept"> <id column="id" property="id"/> <result column="dept_name" property="departmentName"/> <collection property="emps" ofType="com.xiao.pojo.Employee" select="getEmp" column="id"/> </resultMap> <select id="getEmp" resultType="com.xiao.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where d_id = #{id} </select>
將多列的值封裝成map傳遞:column={key1=column1,key2=column2...},使用時則用#{key}獲取
開啓了全局懶加載後,特定關聯關係中可經過設置 fetchType
屬性來覆蓋該項的開關狀態
鑑別器discriminator
相似於 Java 語言中的 switch 語句,能夠根據不一樣的條件封裝不一樣的結果集。須要指定 column 和 javaType 屬性。
<discriminator javaType="string" column="gender"> <case value="0" resultType="com.xiao.pojo.Employee"> .... </case> <case value="0" resultType="com.xiao.pojo.Employee"> .... </case> </discriminator>