Java EE——Mybatis 框架学习
文章目錄
- 一、MyBatis 框架概述:
- 二、 Mybatis 框架入門
- 三、 基于代理 Dao 實現 CRUD 操作
- 1、根據 ID 查詢:
- 2、保存操作:
- 3、用戶更新:
- 4、用戶刪除:
- 四、Mybatis 的參數深入
- 五、Mybatis的輸出結果封裝
- 1、resultType的配置結果類型:
- 六、SqlMapConfig.xml配置文件
- 七、Mybatis 連接池
- 1、Mybatis 的連接池技術:
- 八、Mybatis的動態SQL語句
- 九、Mybatis多表查詢一對一(多對一)
- 十、 Mybatis 多表查詢——多對多
- 十一、 Mybatis 延遲加載策略
- 十二、 Mybatis 緩存
- 十三、 Mybatis 注解開發
一、MyBatis 框架概述:
1、什么是mybatis:
-
mybatis 是一個優秀的基于 java 的持久層框架,它內部封裝了 jdbc,使開發者只需要關注 sql 語句本身,而不需要花費精力去處理加載驅動、創建連接、創建 statement 等繁雜的過程。
-
mybatis 通過 xml 或注解的方式將要執行的各種 statement 配置起來,并通過 java 對象和 statement 中sql 的動態參數進行映射生成最終執行的 sql 語句,最后由 mybatis 框架執行 sql 并將結果映射為 java 對象并返回。
-
采用 ORM 思想解決了實體和數據庫映射的問題,對 jdbc 進行了封裝,屏蔽了 jdbc api 底層訪問細節,使我們不用與 jdbc api 打交道,就可以完成對數據庫的持久化操作。
2、mybatis在三層架構中的位置
二、 Mybatis 框架入門
1、基于XML的方式:
1、創建 maven 工程:
2、添加 Mybatis的坐標:
- 在 pom.xml 文件中添加 Mybatis3.4.5 的坐標,如下:
3、編寫 User 實體類:
public class User implements Serializable {private Integer id;private String username;private Date birthday;private String address;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", address='" + address + '\'' +'}';} }4、編寫持久層接口 IUserDao:
public interface IUserDao {/**查詢所有操作* */List<User> findAll(); }5、編寫持久層接口的映射文件 IUserDao.xml:
注意:
- 創建位置:必須和持久層接口在相同的包中。
- 名稱:必須以持久層接口名稱命名文件名,擴展名是.xml
例:都是在com.smk.dao包中
不要忘記在映射配置中告知mybatis要封裝到哪個實體類中。
配置的方式:指定實體類的全限定類名 即:resultType="com.smk.domain.User"
6、 編寫 SqlMapConfig.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--mybatis的主配置文件--> <configuration><!-- 配置環境--><environments default="mysql"><!-- 配置mysql環境--><environment id="mysql"><!--配置事物的類型--><transactionManager type="JDBC"></transactionManager><!--配置數據源/連接池2--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/my_batis?useUnicode=true&characterEncoding=utf8"/><property name="username" value="root"/><property name="password" value=""/></dataSource></environment></environments><!--指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件--><mappers><mapper resource="com/smk/dao/IUserDao.xml"></mapper></mappers> </configuration>7、編寫測試類:
public class mybatis {public static void main(String[] args) throws IOException {//1.讀取配置文件InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.創建一個SqlsessionFactory工廠SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(in);//3.使用工廠生產一個Sqlsession對象SqlSession session = factory.openSession();//4.使用SqlSession創建Dao接口對象IUserDao userDao = session.getMapper(IUserDao.class);//5.使用代理對象執行方法List<User> users = userDao.findAll();for (User user : users) {System.out.println(user);}//6.釋放資源session.close();in.close();} }在測試類中運行:
2、基于注解的方式:
在基于注解的的方式下就不再需要 IUserDao.xml配置文件,同時需要在SqlMapConfig.xml中的mapper配置時,使用class屬性指定dao接口的全限定類名。
1、在持久層接口中添加注解:
public interface IUserDao {/**查詢所有操作* */@Select("select * from user")List<User> findAll(); }2、修改 SqlMapConfig.xml:
<mappers><mapper class="com.smk.dao.IUserDao"></mapper> </mappers>注意:在使用基于注解的 Mybatis 配置時,須移除 xml 的映射配置(IUserDao.xml)。
運行結果:
補充:
- 不管使用XML還是注解配置, Mybatis是支持寫dao實現類的。
- 但在在實際開發中,都是越簡便越好,所以都是采用不寫dao實現類的方式。
三、 基于代理 Dao 實現 CRUD 操作
1、根據 ID 查詢:
步驟:
1、在持久層接口中添加 findById 方法:
public interface IUserDao {//根據 id 查詢User findById (Integer userId); }2、配置映射文件:
<mapper namespace="com.smk.dao.IUserDao"><select id="findById" resultType="com.smk.domain.User" parameterType="int">select * from user where id= #{id}</select> </mapper>解釋:
- resultType 屬性:用于指定結果集的類型。
- parameterType 屬性:用于指定傳入參數的類型。
- sql 語句中使用#{}字符:它代表占位符,相當于原來 jdbc 部分所學的,都是用于執行語句時替換實際的數據。具體的數據是由#{}里面的內容決定的。
- #{}中內容的寫法:由于數據類型是基本類型,所以此處可以隨意寫。
3、 在測試類添加測試:
public class mybatis {private InputStream in;private SqlSessionFactory factory;private SqlSession session;private IUserDao userDao;@Testpublic void testFindOne() {//6.執行操作User user = userDao.findById(41);System.out.println(user);}@Before//在測試方法執行之前執行public void init() throws Exception {//1.讀取配置文件in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.創建構建者對象SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();//3.創建 SqlSession 工廠對象factory = builder.build(in);//4.創建 SqlSession 對象session = factory.openSession();//5.創建 Dao 的代理對象userDao = session.getMapper(IUserDao.class);}@After//在測試方法執行完成之后執行public void destroy() throws Exception {session.commit();//提交事務;在增刪改時需要提交,查詢時不需要。//7.釋放資源session.close();in.close();} }運行結果:
2、保存操作:
同樣的套路
1、在持久層接口中添加新增方法:
// 保存用戶int saveUser(User user);2、配置映射文件:
<insert id="saveUser" parameterType="com.smk.domain.User">insert into user(username,birthday,sex,address) value (#{username},#{birthday},#{sex},#{address}) </insert>解釋:
#{}中內容的寫法:
- 由于保存方法的參數是 一個 User 對象,此處要寫 User 對象中的屬性名稱。
它用的是 ognl 表達式。
ognl 表達式:
-
它是 apache 提供的一種表達式語言,全稱是:Object Graphic Navigation Language 對象圖導航語言,它是按照一定的語法格式來獲取數據的。語法格式就是使用 #{對象.對象} 的方式。
-
#{user.username} 它會先去找 user 對象,然后在 user 對象中找到 username 屬性,并調用 getUsername() 方法把值取出來。但是我們在 parameterType 屬性上指定了實體類名稱,所以可以省略 user 而直接寫 username。
3、測試:
@Testpublic void testSave() {User user = new User();user.setUsername("modify User property");user.setAddress("北京市順義區");user.setSex("男");user.setBirthday(new Date());System.out.println("保存操作之前:" + user);//5.執行保存方法userDao.saveUser(user);System.out.println("保存操作之后:" + user);}注意:
在實現 增刪改 時需要進行控制事務的提交。
如何在mybatis 中如何控制事務提交?
- 可以使用:session.commit(); 來實現事務提交。加入事務提交后的代碼如下:
運行結果:
問題擴展:獲取新增用戶 id 的返回值:
新增用戶后,同時還要返回當前新增用戶的 id 值,因為 id 是由數據庫的自動增長來實現的,所以就相當于我們要在新增后將自動增長 auto_increment 的值返回。
<insert id="saveUser" parameterType="com.smk.domain.User"><!-- 配置保存時獲取插入的id keypreperty:代表要返回的值對應實體類里的字段 ,keyColumn代表數據庫的字段名--><selectKey keyProperty="id" keyColumn="id" resultType="int">select last_insert_id();</selectKey>insert into user(username,birthday,sex,address) value (#{username},#{birthday},#{sex},#{address})</insert>再次執行測試:
可以看到 id 的值已經獲取到了。
3、用戶更新:
1、在持久層接口中添加更新方法:
//更新用戶int updateUser(User user);2、配置映射文件:
<update id="updateUser" parameterType="com.smk.domain.User">update user set username=#{username},birthday=#{birthday},sex=#{sex}, address=#{address} where id=#{id}</update>3、更新的測試方法:
@Testpublic void testUpdateUser() throws Exception {//1.根據 id 查詢User user = userDao.findById(52);//2.更新操作user.setAddress("北京市順義區");int res = userDao.updateUser(user);System.out.println(res);}運行結果:
4、用戶刪除:
1、在持久層接口中添加刪除方法:
//刪除用戶int deleteUser(int uid);2、配置映射文件:
<delete id="deleteUser" parameterType="int">delete from user where id=#{uid} </delete>3、刪除的測試方法:
@Testpublic void testdeleteUser(){int res = userDao.deleteUser(52);System.out.println(res);}至此基于mybatis的增刪改查就完成了。
四、Mybatis 的參數深入
注:Mybatis使用ognl表達式解析對象字段的值,#{}或${}括號中的值為pojo屬性名稱。
什么是ognl:
Object Graphic Navigation Language
??對象 ????圖??????????導航 ????????語言
它是通過對象的取值方法來獲取數據。在寫法上把get給省略了。
比如:我們獲取用戶的名稱
- 類中的寫法:user.getUsername()
- OGNL表達式寫法:user.username
mybatis中為什么能直接寫username,而不用user.呢?
- 因為在parameterType中已經提供了屬性所屬的類,所以此時不需要寫對象名。
1、parameterType 配置參數:
SQL 語句的傳參是通過使用標簽的 parameterType 屬性來設定。該屬性的取值可以是基本類型,引用類型(例如:String 類型),還可以是實體類類型(POJO 類)。同時也可以使用實體類的包裝類。
2、傳遞 pojo 包裝對象:
開發中通過 pojo 傳遞查詢條件 。當查詢條件是綜合的查詢條件:不僅包括用戶查詢條件還包括其它的查詢條件(比如將用戶購買商品信息也作為查詢條件),這時可以使用包裝對象傳遞輸入參數。即: Pojo 類中包含 pojo 。
例:
需求:根據用戶名查詢用戶信息,查詢條件放到 QueryVo 的 user 屬性中。
1、編寫 QueryVo:
public class QueryVo {private User user;public User getUser() {return user;}public void setUser(User user) {this.user = user;} }2、編寫持久層接口:
//根據 QueryVo 中的條件查詢用戶List<User> findByVo(QueryVo vo);3、配置映射文件:
<!-- 根據用戶名稱模糊查詢,參數變成一個 QueryVo 對象了 --><select id="findByVo" resultType="com.smk.domain.User" parameterType="com.smk.domain.QueryVo">select * from user where username like #{user.username};</select>4、添加測試類:
@Testpublic void testFindByQueryVo() {QueryVo vo = new QueryVo();User user = new User();user.setUsername("%王%");vo.setUser(user);List<User> users = userDao.findByVo(vo);for (User u : users) {System.out.println(u);}}運行結果:
五、Mybatis的輸出結果封裝
1、resultType的配置結果類型:
resultType 屬性可以指定結果集的類型,它支持基本數據類型和實體類數據類型。
它和 parameterType 一樣,如果注冊過類型別名的,可以直接使用別名。
沒有注冊過的必須使用全限定類名。
例如:我們的實體類此時必須是全限定類名,同時,當是實體類名稱時,實體類中的屬性名稱必須和查詢語句中的列名(即數據庫里的表名列名)保持一致,否則無法實現封裝。
特殊情況示例:
當實體類屬性和數據庫表的列名已經不一致時
1、實體類代碼:
public class User2 {public class User implements Serializable {private Integer userId;private String userName;private Date userBirthday;private String userSex;private String userAddress;public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Date getUserBirthday() {return userBirthday;}public void setUserBirthday(Date userBirthday) {this.userBirthday = userBirthday;}public String getUserSex() {return userSex;}public void setUserSex(String userSex) {this.userSex = userSex;}public String getUserAddress() {return userAddress;}public void setUserAddress(String userAddress) {this.userAddress = userAddress;}@Overridepublic String toString() {return "User [userId=" + userId + ", userName=" + userName + ", userBirthday="+ userBirthday + ", userSex="+ userSex + ", userAddress=" + userAddress + "]";}}}2、映射配置:
// 查詢所有用戶List<User> findAll();3、測試查詢結果:
@Testpublic void testFindAll() {List<User2> users = userDao.findAll();for (User2 user : users) {System.out.println(user);}}運行結果:
為什么名稱會有值呢?
因為:mysql 在 windows 系統中不區分大小寫!
應對措施:
1、使用別名查詢:
即給實體類的屬性名起對應數據的字段值別名
2、resultMap 結果類型:
resultMap 標簽可以建立查詢的列名和實體類的屬性名稱不一致時建立對應關系。從而實現封裝。
在 select 標簽中使用 resultMap 屬性指定引用即可。同時 resultMap 可以實現將查詢結果映射為復雜類型的 pojo,比如在查詢結果映射對象中包括 pojo 和 list 實現一對一查詢和一對多查詢。
實現步驟:
1、定義 resultMap:
<!-- 建立 Use實體和數據庫表的對應關系type屬性:指定實體類的全限定類名id 屬性:給定一個唯一標識,是給查詢 select 標簽引用用的--><resultMap type="com.smk.domain.User2" id="userMap"><id column="id" property="userId"/><result column="username" property="userName"/><result column="sex" property="userSex"/><result column="address" property="userAddress"/><result column="birthday" property="userBirthday"/></resultMap>- id 標簽:用于指定主鍵字段
- result 標簽:用于指定非主鍵字段
- column 屬性:用于指定數據庫列名
- property 屬性:用于指定實體類屬性名稱
2、映射配置:
<!-- 配置查詢所有操作 --><select id="findAll" resultMap="userMap">select * from user</select>3、測試結果:
可以看到查詢正常了。
六、SqlMapConfig.xml配置文件
1、properties(屬性):
在使用 properties 標簽配置時,我們可以采用兩種方式指定屬性配置。
1、第一種 :
<properties><property name="jdbc.driver" value="com.mysql.jdbc.Driver"/><property name="jdbc.url"value="jdbc:mysql://localhost:3306/my_batis?useUnicode=true&characterEncoding=utf8"/><property name="jdbc.username" value="root"/><property name="jdbc.password" value=""/></properties>2、第二種 :
在 classpath (類路徑,resources目錄即為根 類路徑) 下定義 db.properties 文件
db.properties 文件內容:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/my_batis?useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=注意:db.properties 文件中 數據庫連接,指定編碼格式不能用 & 需改為 &
配置信息:
<properties resource="db.properties"> </properties>此時我們的 dataSource 標簽就變成了引用上面的配置:
<!--配置數據源/連接池--><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="{$jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource>2、typeAliases(類型別名):、
自定義別名:
在 SqlMapConfig.xml 中配置:
<typeAliases> <!-- 單個別名定義 --> <typeAlias type="com.itheima.domain.User" alias="user"/><!-- 批量別名定義,掃描整個包下的類,別名為類名(首字母大寫或小寫都可以)--> <package name="com.smk.domain"/> <package name="其它包"/> </typeAliases>3、mappers(映射器):
<mapper resource=" "/> 使用相對于類路徑的資源,如:
<mapper resource="com/smk/dao/IUserDao.xml" /><mapper class=" "/> 使用 mapper 接口類路徑,如:
<mapper class="com.smk.dao.UserDao"/>package 標簽是用于指定dao接口所在的包,當指定了之后,就不需要再寫 mapper標簽以及 resource 或者 class 了
在 SqlMapConfig.xml 中配置:
<package name=""/> 注冊指定包下的所有 mapper 接口,如:
<mappers><package name="com.smk.dao"/> </mappers>注意:此種方法要求 mapper 接口名稱和 mapper 映射文件名稱相同,且放在同一個目錄中。
七、Mybatis 連接池
1、Mybatis 的連接池技術:
在 web 開發中,常使用連接池技術來減少我們獲取連接所消耗的時間;在 Mybatis 中也有連接池技術,但是它采用的是自己的連接池技術。在 Mybatis 的 SqlMapConfig.xml 配置文件中,通過 <dataSource type = ""> 來實現 Mybatis 中連接池的配置。
1.1、Mybatis 連接池的分類:
mybatis連接池提供了3種方式的配置:
配置的位置:
- 主配置文件SqlMapConfig.xml中的dataSource標簽,type屬性就是表示采用何種連接池方式。
type屬性的取值:
- POOLED 使用連接池的數據源
- UNPOOLED 不使用連接池的數據源
- JNDI 使用 JNDI 實現的數據源
具體結構如下:
相應地,MyBatis 內部分別定義了實現了 java.sql.DataSource 接口的 UnpooledDataSource,PooledDataSource 類來表示 UNPOOLED、POOLED 類型的數據源。
1.2、Mybatis 中數據源的配置:
在SqlMapConfig.xml 進行配置
<dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/> </dataSource>Mybatis 在初始化時,根據<dataSource>的 type 屬性來創建相應類型的的數據源 DataSource,即:
- type=" POOLED ":MyBatis 會創建 PooledDataSource 實例。
- type=" UNPOOLED ": MyBatis 會創建 UnpooledDataSource 實例。
- type=" JNDI ":MyBatis 會從 JNDI 服務上查找 DataSource 實例,然后返回使用。
八、Mybatis的動態SQL語句
1、動態sql之<if>標簽:
- 根據實體類的不同取值,使用不同的 SQL 語句來進行查詢。比如在 id 如果不為空時可以根據 id 查詢,如果 username 不為空時還要加入用戶名作為條件。這種情況在我們的多條件組合查詢中經常會碰到。
1、持久層 Dao 接口:
//根據用戶信息,查詢用戶列表List<User> findByUser(User user);2、持久層 Dao 映射配置:
<select id="findByUser" resultMap="userMap" parameterType="user">select * from user where 1=1<if test="userName!=null and userName != '' ">and username like #{userName}</if><if test="userAddress != null">or address like #{userAddress}</if></select>注:where 1=1 是為了避免 where 關鍵字后面的第一個詞直接就是 " and " 而導致語法錯誤。
3、 測試:
@Testpublic void testFindByUser() {User u = new User();u.setUserName("%王%");u.setUserAddress("%順義%");//6.執行操作List<User> users = userDao.findByUser(u);for (User user : users) {System.out.println(user);}}運行結果:
可以看到,帶 “王” 的用戶和地址帶 “順義” 的用戶信息全查詢出來了。
2、動態sql 之 <where>標簽:
為了簡化上面 where 1=1 的條件拼裝,可以采用 <where> 標簽來簡化開發。
1、持久層 Dao 映射配置:
<select id="findByUser" resultMap="userMap" parameterType="user">select * from user<where><if test="userName!=null and userName != '' ">and username like #{userName}</if><if test="userAddress != null">or address like #{userAddress}</if></where></select>3、動態標簽之<foreach>標簽:
在進行范圍查詢時,就要將一個集合中的值,作為參數動態添加進來。
這樣將如何進行參數的傳遞?
1、在 QueryVo 中加入一個 List 集合用于封裝參數:
public class QueryVo {private User user;private List<Integer> ids;public List<Integer> getIds() {return ids;}public void setIds(List<Integer> ids) {this.ids = ids;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}}2、持久層 Dao 接口:
// 根據 id 集合查詢用戶List<User> findInIds(QueryVo vo);3、持久層 Dao 映射配置:
<select id="findInIds" resultMap="userMap" parameterType="queryVo">select * from user<where><if test='ids !=null and ids.size>0'><foreach collection="ids" open="id in (" close=")" item="id" separator=",">#{id}</foreach></if></where></select>SQL 語句:
- select 字段 from user where id in (?)
<foreach> 標簽用于遍歷集合,它的屬性:
- collection:代表要遍歷的集合元素,注意編寫時不要寫#{}
- open:代表語句的開始部分
- close:代表結束部分
4、 測試:
@Testpublic void testFindInIds() {QueryVo vo = new QueryVo();List<Integer> ids = new ArrayList<Integer>();ids.add(41);ids.add(42);ids.add(43);ids.add(46);ids.add(57);vo.setIds(ids);//6.執行操作List<User> users = userDao.findInIds(vo);for (User user : users) {System.out.println(user);}}運行結果:
4、Mybatis 中簡化編寫的 sql 片段:
Sql 中可將重復的 sql語句 提取出來,使用時用 include 引用即可,最終達到 sql 重用的目的。
1、定義代碼片段:
<!-- 抽取重復的語句代碼片段 --><sql id="defaultSql">select * from user</sql>2、引用代碼片段:
<!-- 配置查詢所有操作 --><select id="findAll" resultType="user"><include refid="defaultSql"></include></select><!-- 根據 id 查詢 --><select id="findById" resultType="UsEr" parameterType="int"><include refid="defaultSql"></include>where id = #{uid}</select>九、Mybatis多表查詢一對一(多對一)
1、一對一查詢(多對一):
以最為簡單的用戶和賬戶的模型來分析 Mybatis 多表關系。用戶為 User 表,賬戶為Account表。
一個用戶(User)可以有一個或多個賬戶(Account)。
需求:
查詢所有賬戶信息,關聯查詢下單用戶信息(只要用戶名和地址)。
注意:
因為一個賬戶信息只能供某一個用戶使用,所以從查詢賬戶信息出發,關聯查詢用戶信息為一對一查詢。如果從用戶信息出發,查詢用戶下的賬戶信息則為一對多查詢,因為一個用戶可以有多個賬戶。
方式一:
1、定義賬戶信息的實體類:
public class Account implements Serializable {private Integer id;private Integer uid;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", uid=" + uid +", money=" + money +'}';} }2、定義 AccountUser 類:
為了能夠封裝上面 SQL 語句的查詢結果,定義 AccountCustomer 類中要包含賬戶信息同時還要包含用戶信息,所以在定義 AccountUser 類時可以繼承 User 類。
public class AccountUser extends Account implements Serializable {private String username;private String address;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return super.toString() + " AccountUser{" +"username='" + username + '\'' +", address='" + address + '\'' +'}';} }3、定義持久層 Dao 接口:
/*** 查詢所有用戶,帶有用戶名和地址信息* @return*/List<AccountUser> findAllAccount();4、定義 IAccountDao.xml 文件中的查詢配置信息:
<!--查詢所有賬戶同是包含用戶名和地址信息--><select id="findAllAccount" resultType="accountuser">select a.*,u.username,u.address from account a,user u where a.uid =u.id;</select>5、創建 AccountTest 測試類:
@Testpublic void testFindAllAccount() {List<AccountUser> aus = accountDao.findAllAccount();for (AccountUser au : aus) {System.out.println(au);}}運行結果:
小結:
定義專門的 po 類作為輸出類型,其中定義了 sql 查詢結果集所有的字段。此方法較為簡單,企業中使用普遍。
po: (persistant object)持久對象,可以看成是與數據庫中的表相映射的java對象。
方式二:
使用 resultMap,定義專門的 resultMap 用于映射一對一查詢結果。
我們可以在 Account 類中加入一個 User 類的對象來代表這個賬戶是哪個用戶的。
1、在 Account 類中加入 User 類的對象作為 Account 類的一個屬性:
public class Account implements Serializable {private Integer id;private Integer uid;private Double money;private User user;public User getUser() {return user;}public void setUser(User user) {this.user = user;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", uid=" + uid +", money=" + money +'}';} }2、修改 AccountDao 接口中的方法:
List<Account> findAllAccount();把 AccountUser 改為 Account
3、重新定義 AccountDao.xml 文件:
<!-- 建立對應關系 --><resultMap type="account" id="accountMap"><id column="aid" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/><!-- 它是用于指定 從表 方的引用實體屬性的 --><association property="user" javaType="user"><id column="id" property="id"/><result column="username" property="username"/><result column="sex" property="sex"/><result column="birthday" property="birthday"/><result column="address" property="address"/></association></resultMap><select id="findAllAccount" resultMap="accountMap">select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;</select>注:
- a.id as aid :這里兩個表的字段重復了所以需要起別名。
- javaType=" user ":即指定數據類型為user類,全限定類名或者別名(此處為別名)。
4、加入測試方法:
@Testpublic void testFindAllAccount() {List<Account> accounts = accountDao.findAllAccount();for (Account account : accounts) {System.out.println(account);System.out.println(account.getUser());}}測試結果:
2、 一對多查詢:
需求:
- 查詢所有用戶信息及用戶關聯的賬戶信息。
分析:
- 用戶信息和他的賬戶信息為一對多關系,并且查詢過程中如果用戶沒有賬戶信息,此時也要將用戶信息查詢出來,此時應該使用左外連接查詢比較合適。
1、User 類加入 List <Account>:
public class User implements Serializable {private Integer Id;private String username;private String Address;private String Sex;private Date Birthday;private List<Account> accounts;public List<Account> getAccounts() {return accounts;}public void setAccounts(List<Account> accounts) {this.accounts = accounts;}public Integer getId() {return Id;}public void setId(Integer Id) {this.Id = Id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getAddress() {return Address;}public void setAddress(String address) {Address = address;}public String getSex() {return Sex;}public void setSex(String sex) {Sex = sex;}public Date getBirthday() {return Birthday;}public void setBirthday(Date birthday) {Birthday = birthday;}@Overridepublic String toString() {return "User{" +"Id=" + Id +", username='" + username + '\'' +", Address='" + Address + '\'' +", Sex='" + Sex + '\'' +", Birthday=" + Birthday +'}';} }2、 Dao 接口中加入查詢方法:
List<User> findAll();3、Dao 映射文件配置:
<resultMap type="user" id="userMap"><id column="id" property="id"></id><result column="username" property="username"/><result column="address" property="address"/><result column="sex" property="sex"/><result column="birthday" property="birthday"/><!-- collection 是用于建立一對多中集合屬性的對應關系ofType 用于指定集合元素的數據類型--><collection property="accounts" ofType="account"><id column="aid" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/></collection></resultMap><!--查詢所有--><select id="findAll" resultMap="userMap">select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid</select>注:
collection:
- 部分定義了用戶關聯的賬戶信息。表示關聯查詢結果集
property=" accList " :
- 關聯查詢的結果集存儲在 User 對象的上哪個屬性。
ofType=" account ":
- 指定關聯查詢的結果集中的對象類型即List中的對象類型。此處可以使用別名,也可以使用全限定名。
4、 測試方法:
@Testpublic void testFindAll() {List<User> users = userDao.findAll();for (User user : users) {System.out.println("用戶的信息:");System.out.println(user);System.out.println(user.getAccounts());}}5、運行結果:
十、 Mybatis 多表查詢——多對多
1、實現 Role 到 User 多對多:
1、編寫 Role 實體類:
public class Role implements Serializable {private Integer roleId;private String roleName;private String roleDesc;private List<User> users;public List<User> getUsers() {return users;}public void setUsers(List<User> users) {this.users = users;}public Integer getRoleId() {return roleId;}public void setRoleId(Integer roleId) {this.roleId = roleId;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public String getRoleDesc() {return roleDesc;}public void setRoleDesc(String roleDesc) {this.roleDesc = roleDesc;}@Overridepublic String toString() {return "Role{" +"roleId=" + roleId +", roleName='" + roleName + '\'' +", roleDesc='" + roleDesc + '\'' +'}';} }2、編寫 Role 持久層接口:
/*** 查詢所有角色* @return*/List<Role> findAll();3、編寫映射文件:
<mapper namespace="com.itheima.dao.IRoleDao"><!--定義role表的ResultMap--><resultMap id="roleMap" type="role"><id property="roleId" column="rid"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result><collection property="users" ofType="user"><id column="id" property="id"></id><result column="username" property="username"></result><result column="address" property="address"></result><result column="sex" property="sex"></result><result column="birthday" property="birthday"></result></collection></resultMap><!--查詢所有--><select id="findAll" resultMap="roleMap">select u.*,r.id as rid,r.role_name,r.role_desc from role rleft outer join user_role ur on r.id = ur.ridleft outer join user u on u.id = ur.uid</select> </mapper>4、測試類:
/*** 測試查詢所有*/@Testpublic void testFindAll() {List<Role> roles = roleDao.findAll();for (Role role : roles) {System.out.println("---每個角色的信息----");System.out.println(role);System.out.println(role.getUsers());}}運行結果:
2、實現 User 到 Role 的多對多:
大體步驟和上面同理,其中sql語句需改為:
select u.*,r.id as rid,r.role_name,r.role_desc from user uleft outer join user_role ur on u.id = ur.ridleft outer join role r on r.id = ur.uid十一、 Mybatis 延遲加載策略
通過前面的學習,我們已經掌握了 Mybatis 中一對一,一對多,多對多關系的配置及實現,可以實現對象的關聯查詢。實際開發過程中很多時候我們并不需要總是在加載用戶信息時就一定要加載他的賬戶信息。此時就是我們所說的延遲加載。
1、何為延遲加載?
就是在需要用到數據時才進行加載,不需要用到數據時就不加載數據。延遲加載也稱懶加載。
好處:
- 先從單表查詢,需要時再從關聯表去關聯查詢,大大提高數據庫性能,因為查詢單表要比關聯查詢多張表速度要快。
壞處:
- 因為只有當需要用到數據時,才會進行數據庫查詢,這樣在大批量數據查詢時,因為查詢工作也要消耗時間,所以可能造成用戶等待時間變長,造成用戶體驗下降。
2、使用 assocation 實現延遲加載:
需求:
- 查詢賬戶(Account) 信息并且關聯查詢用戶(User) 信息。如果先查詢賬戶(Account) 信息即可滿足要求,當我們需要查詢用戶(User) 信息時再查詢用戶(User) 信息。把對用戶(User) 信息的按需去查詢就是延遲加載。
1、DAO 接口:
List<Account> findAll();2、持久層映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.itheima.dao.IAccountDao"><!-- 建立對應關系 --><resultMap type="account" id="accountMap"><id column="id" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/><!-- 它是用于指定從表方的引用實體屬性的 --><association property="user" javaType="user"select="com.itheima.dao.IUserDao.findById"column="uid"></association></resultMap><select id="findAll" resultMap="accountMap">select * from account</select> </mapper>其中:
- select: 填寫我們要調用的 select 映射的 id
- column : 填寫我們要傳遞給 select 映射的參數
3、User 的持久層接口和映射文件:
User findById(Integer userId); <!--根據 id 查詢--> <select id="findById" resultType="user" parameterType="int" >select * from user where id = #{uid} </select>4、開啟 Mybatis 的延遲加載策略:
在 SqlMapConfig.xml 中配置
<!--開啟延遲加載的支持--><settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/></settings>- lazyLoadingEnabled:延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。
- aggressiveLazyLoading:當開啟時,任何方法的調用都會加載該對象的所有屬性。否則,每個屬性會按需加載。
5、測試查詢用戶信息時:
@Testpublic void testFindAll() {List<Account> accounts = accountDao.findAll();for (Account account : accounts) {System.out.println("每個account的信息:--------------");System.out.println(account);System.out.println(account.getUser());}}測試結果:
可以看到SQL語句全部執行了。
6、測試只查賬戶信息不查用戶信息:
@Testpublic void testFindAll() {List<Account> accounts = accountDao.findAll();/* for (Account account : accounts) {System.out.println("每個account的信息:--------------");System.out.println(account);System.out.println(account.getUser());}*/}運行結果:
可以看到只執行了一條SQL語句,即執行了延遲加載。
3、使用 Collection 實現延遲加載:
同樣我們也可以在一對多關系配置的<collection>結點中配置延遲加載策略。<collection>結點中也有 select 屬性,column 屬性。
需求:
- 完成加載用戶對象時,查詢該用戶所擁有的賬戶信息。
1、 User 實體類:
public class User implements Serializable {private Integer Id;private String username;private String Address;private String Sex;private Date Birthday;private List<Account> accounts;public List<Account> getAccounts() {return accounts;}public void setAccounts(List<Account> accounts) {this.accounts = accounts;}public Integer getId() {return Id;}public void setId(Integer Id) {this.Id = Id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getAddress() {return Address;}public void setAddress(String address) {Address = address;}public String getSex() {return Sex;}public void setSex(String sex) {Sex = sex;}public Date getBirthday() {return Birthday;}public void setBirthday(Date birthday) {Birthday = birthday;}@Overridepublic String toString() {return "User{" +"Id=" + Id +", username='" + username + '\'' +", Address='" + Address + '\'' +", Sex='" + Sex + '\'' +", Birthday=" + Birthday +'}';} }2、用戶(User)和賬戶(Account)持久層接口的方法:
/*** 查詢所有用戶** @return*/List<User> findAll();/*** 根據用戶 id 查詢賬戶信息* @param uid* @return*/List<User> findByUid(Integer uid);3、編寫用戶持久層映射配置:
<resultMap type="user" id="userMap"><id column="id" property="id"></id><result column="username" property="username"/><result column="address" property="address"/><result column="sex" property="sex"/><result column="birthday" property="birthday"/><!-- collection 是用于建立一對多中集合屬性的對應關系 ofType 用于指定集合元素的數據類型--><collection property="accounts" ofType="account"select="com.itheima.dao.IAccountDao.findByUid"column="id"></collection></resultMap><!--查詢所有--><select id="findAll" resultMap="userMap">select * from user</select><collection>標簽:
- 主要用于加載關聯的集合對象
select 屬性:
- 用于指定查詢 account 列表的 sql 語句,所以填寫的是該 sql 映射的 id
column 屬性:
- 用于指定 select 屬性的 sql 語句的參數來源,上面的參數來自于 user 的 id 列,所以就寫成 id 這一個字段名了
4、編寫賬戶持久層映射配置:
<!-- 根據用戶 id 查詢賬戶信息 --><select id="findByUid" resultType="account" parameterType="int">select * from account where uid = #{uid}</select>5、延時加載 測試只加載用戶信息:
@Testpublic void testFindAll() {List<User> users = userDao.findAll();/* for (User user : users) {System.out.println("用戶的信息:");System.out.println(user);System.out.println(user.getAccounts());}*/}測試結果:
可以發現并沒有加載 Account 賬戶信息。
十二、 Mybatis 緩存
像大多數的持久化框架一樣,Mybatis 也提供了緩存策略,通過緩存策略來減少數據庫的查詢次數,從而提高性能。
Mybatis 中緩存分為一級緩存,二級緩存。
1、Mybatis 一級緩存:
一級緩存是 SqlSession 級別的緩存,只要 SqlSession 沒有 flush 或 close,它就存在。
驗證一級緩存的存在:
測試方法:
@Testpublic void testFirstCache() {User user1 = userDao.findById(41);System.out.println(user1);User user2 = userDao.findById(41);System.out.println(user2);System.out.println(user1 == user2);}測試結果:
可以發現,雖然在上面的代碼中查詢了兩次,但最后只執行了一次數據庫操作,這就是 Mybatis 提供給我們的一級緩存在起作用了。因為一級緩存的存在,導致第二次查詢 id 為 41 的記錄時,并沒有發出 sql 語句從數據庫中查詢數據,而是從一級緩存中查詢。
測試一級緩存的清空:
測試方法:
@Testpublic void testFirstCache() {User user1 = userDao.findById(41);System.out.println(user1);// sqlSession.close();//再次獲取 SqlSession 對象// sqlSession = factory.openSession();//userDao = sqlSession.getMapper(IUserDao.class);sqlSession.clearCache();//此方法也可以清空緩存User user2 = userDao.findById(41);System.out.println(user2);System.out.println(user1 == user2);}測試結果:
當執行 sqlSession.clearCache() 后,再次獲取sqlSession并查詢id=41的User對象時,又重新執行了sql語句,從數據庫進行了查詢操作。
一級緩存的分析:
一級緩存是 SqlSession 范圍的緩存,當調用 SqlSession 的修改,添加,刪除,commit(),close()方法時,就會清空一級緩存。
當第一次發起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息,如果沒有,從數據庫查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。
如果 sqlSession 去執行 commit 操作(執行插入、更新、刪除),清空 SqlSession 中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。
第二次發起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息(此時已經沒有一級緩存了),如果緩存中有,則直接從緩存中獲取用戶信息,沒有則重新發起查詢。
2、Mybatis 二級緩存:
它是 Mybatis 中 SqlSessionFactory 對象的緩存。由同一個 SqlSessionFactory 對象創建的 SqlSession 共享其緩存。
假設:
sqlSession1 去查詢用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。
如果 SqlSession3 去執行相同 mapper 映射下 sql,執行 commit 提交,將會清空該 mapper 映射下的二級緩存區域的數據。
sqlSession2 去查詢與 sqlSession1 相同的用戶信息,首先會去緩存中找是否存在數據,如果存在直接從緩存中取出數據,,否則重新發起查詢(本例)。
二級緩存的使用步驟:
1、讓Mybatis框架支持二級緩存(在SqlMapConfig.xml中配置):
<settings><!-- 開啟二級緩存的支持 --><setting name="cacheEnabled" value="true"/> </settings>因為 cacheEnabled 的取值默認就為 true,所以這一步可以省略不配置。
true 代表開啟二級緩存;為false 代表不開啟二級緩存。
2、讓當前的映射文件支持二級緩存(在IUserDao.xml中配置):
<!-- 開啟二級緩存的支持 --><cache/>3、讓當前的操作支持二級緩存(在select標簽中配置):
<!-- 根據 id 查詢 --> <select id="findById" resultType="user" parameterType="int" useCache="true">select * from user where id = #{uid} </select>將 IUserDao.xml 映射文件中的<select>標簽中設置 useCache=" true " 代表當前這個 statement 要使用二級緩存,如果不使用二級緩存可以設置為 false。
注意:針對每次查詢都需要最新的數據 sql,要設置成 useCache=false,禁用二級緩存。
測試:
@Testpublic void testFirstLevelCache(){SqlSession sqlSession1 = factory.openSession();IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);User user1 = dao1.findById(41);System.out.println(user1);sqlSession1.close();//一級緩存消失SqlSession sqlSession2 = factory.openSession();IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);User user2 = dao2.findById(41);System.out.println(user2);sqlSession2.close();System.out.println(user1 == user2);}運行結果:
經過上面的測試,發現執行了兩次查詢,并且在執行第一次查詢后,我們關閉了一級緩存,再去執行第二次查詢時,我們發現并沒有對數據庫發出 sql 語句,所以此時的數據就只能是來自于我們所說的二級緩存。
user1 = user2 = false 的原因是 二級緩存存放的是 數據 而不是對象,每次查詢都會創建一個新的對象。
十三、 Mybatis 注解開發
1、mybatis 的常用注解:
@Insert: 實現新增 @Update: 實現更新 @Delete: 實現刪除 @Select: 實現查詢@Result: 實現結果集封裝 @Results: 可以與@Result 一起使用,封裝多個結果集 @ResultMap: 實現引用@Results 定義的封裝@One: 實現一對一結果集封裝 @Many: 實現一對多結果集封裝@SelectProvider: 實現動態 SQL 映射 @CacheNamespace: 實現注解二級緩存的使用2、使用 Mybatis 注解實現基本 CRUD:
1、實體類:
public class User implements Serializable {private Integer id;private String username;private String address;private String sex;private Date birthday;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", address='" + address + '\'' +", sex='" + sex + '\'' +", birthday=" + birthday +'}';} }2、使用注解方式開發持久層接口:
public interface IUserDao {/*** 查詢所有* @return*/@Select("select * from user")List<User> findAll();/*** 根據 id 查詢一個用戶* @param userId* @return*/@Select("select * from user where id = #{uid} ")User findById(Integer userId);/*** 插入操作* @param user* @return*/@Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")int saveUser(User user);/*** 更新操作* @param user* @return*/@Update("update user set username=#{username}, address =#{address}, sex =#{sex}, birthday =#{birthday}where id =#{id}")int updateUser(User user);/*** 刪除用戶* @param userId* @return*/@Delete("delete from user where id = #{uid} ")int deleteUser(Integer userId);/*** 查詢使用聚合函數* @return*/@Select("select count(*) from user ")int findTotal();/*** 模糊查詢* @param name* @return*/@Select("select * from user where username like #{username} ")List<User> findByName(String name); }3、SqlMapConfig 配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!--引入外部文件--><properties resource="jdbcConfig.properties"/><!--配置別名--><typeAliases><package name="com.smk.domain"/></typeAliases><environments default="mysql"><environment id="mysql"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!-- 配置 dao 接口的位置,它有兩種方式第一種:使用 mapper 標簽配置 class 屬性第二種:使用 package 標簽,直接指定 dao 接口所在的包--><mappers><package name="com.smk.dao"/></mappers> </configuration>4、測試方法:
測試方法和xml映射配置開發是一樣的。
3、使用注解實現復雜關系映射開發:
實現復雜關系映射之前,可以在映射文件中通過配置 <resultMap> 來實現,在使用注解開發時我們需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解。
3.1、當POJO屬性和數據庫字段不一致時:
例:
1、 實體類:
2、dao接口:
public interface IUserDao {/*** 查詢所有** @return*/@Select("select * from user")@Results(id = "userMap", value = {@Result(id = true, column = "id", property = "userId"),@Result(column = "username", property = "userName"),@Result(column = "address", property = "userAddress"),@Result(column = "sex", property = "userSex"),@Result(column = "birthday", property = "userBirthday")})List<User> findAll();/*** 根據 id 查詢用戶** @param userId* @return*/@Select("select * from user where id = #{uid} ")@ResultMap("userMap")User findById(Integer userId);/*** 模糊查詢** @param name* @return*/@Select("select * from user where username like #{username}")@ResultMap("userMap")List<User> findByName(String name); }其中:
@Results 注解代替的是標簽<resultMap>
@Resutl 注解代替了 <id>標簽和<result>標簽
@Result 中 屬性介紹:
- id 是否是主鍵字段
- column 數據庫的列名
- property 需要裝配的屬性名
@Result定義后,在其他方法處,便可以使用該@Results定義的resultMap了。
3.2、使用注解實現一對一復雜關系映射:
1、User 實體類及 Account 實體類:
和上面xml配置方式相同。
2、賬戶的持久層接口及注解配置:
public interface IAccountDao {@Select("select * from account")@Results(id = "accountMap", value = {@Result(id = true, column = "id", property = "id"),@Result(column = "uid", property = "uid"),@Result(column = "money", property = "money"),@Result(property = "user", column = "uid", one = @One(select = "com.smk.dao.IUserDao.findById", fetchType = FetchType.EAGER))})List<Account> findAll(); }@One 注解(一對一):
- 代替了<assocation>標簽,是多表查詢的關鍵,在注解中用來指定子查詢返回單一對象。
@One 注解屬性介紹:
- select 指定用來多表查詢的 sqlmapper
- fetchType 會覆蓋全局的配置參數 lazyLoadingEnabled。。
使用格式:
- @Result(column="",property="",one=@One(select=""))
3、用戶的持久層接口及注解配置:
public interface IUserDao {/*** 查詢所有** @return*/@Select("select * from user")@Results(id = "userMap", value = {@Result(id = true, column = "id", property = "userId"),@Result(column = "username", property = "userName"),@Result(column = "address", property = "userAddress"),@Result(column = "sex", property = "userSex"),@Result(column = "birthday", property = "userBirthday")})List<User> findAll();/*** 根據 id 查詢用戶** @param userId* @return*/@Select("select * from user where id = #{uid} ")@ResultMap("userMap")User findById(Integer userId); }運行結果:
3.3、使用注解實現一對多復雜關系映射:
1、User 實體類及 Account 實體類:
- 和上面xml配置方式相同。
2、用戶的持久層接口及注解配置:
public interface IUserDao {/*** 查詢所有** @return*/@Select("select * from user")@Results(id = "userMap", value = {@Result(id = true, column = "id", property = "userId"),@Result(column = "username", property = "userName"),@Result(column = "address", property = "userAddress"),@Result(column = "sex", property = "userSex"),@Result(column = "birthday", property = "userBirthday"),@Result(property = "accounts", column = "id", many = @Many(select = "com.smk.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))})List<User> findAll(); }3、賬戶的持久層接口及注解配置:
public interface IAccountDao {// 根據用戶 id 查詢用戶下的所有賬戶@Select("select * from account where uid = #{uid} ")List<Account> findByUid(Integer userId); }4、添加測試方法:
//測試查詢所有@Testpublic void testFindAll() {List<User> users = userDao.findAll();for (User user : users) {System.out.println("-----每個用戶的內容-----");System.out.println(user);System.out.println(user.getAccounts());}}運行結果:
測試延時加載:
運行結果:
3.4、mybatis 基于注解的二級緩存:
1、在 SqlMapConfig 中開啟二級緩存支持:
<!-- 配置二級緩存 --> <settings><!-- 開啟二級緩存的支持 --><setting name="cacheEnabled" value="true"/> </settings>2、在持久層接口中使用注解配置二級緩存:
@CacheNamespace(blocking=true)//mybatis 基于注解方式實現配置二級緩存 public interface IUserDao {}🆗,至此mybatis的內容就總結完了( ̄︶ ̄)↗
總結
以上是生活随笔為你收集整理的Java EE——Mybatis 框架学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java EE——SpringMVC框架
- 下一篇: 如何攻击Java Web应用【转载】