Mybatis(黑馬)
1.框架概述
課程介紹

三層架構和ssm框架的對應關系

jdbc操作數據庫的問題分析

- jdbc代碼回顧
java
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加載數據庫驅動
Class.forName("com.mysql.jdbc.Driver");
//通過驅動管理類獲取數據庫鏈接
connection = DriverManager
.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro
ot", "root");
//定義 sql 語句 ?表示占位符
String sql = "select * from user where username = ?";
傳智播客——專注于 Java、.Net 和 Php、網頁平面設計工程師的培訓
北京市昌平區建材城西路金燕龍辦公樓一層 電話:400-618-9090
//獲取預處理 statement
preparedStatement = connection.prepareStatement(sql);
//設置參數,第一個參數為 sql 語句中參數的序號(從 1 開始),第二個參數為設置的
參數值
preparedStatement.setString(1, "王五");
//向數據庫發出 sql 執行查詢,查詢出結果集
resultSet = preparedStatement.executeQuery();
//遍歷查詢結果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+"
"+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//釋放資源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上邊使用 jdbc 的原始方法(未經封裝)實現了查詢數據庫表記錄的操作
在傳統的jdbc中,我們為啥實現查詢這個功能,需要配置獲取連接,獲取預處理對象,關閉資源這些操作。但是這些操作是重復的。我們程序員應該將個更多的精力放在解決需求(sql語句(使用sql語句實現需求))上面去,而不是放在這些非業務的操作上去。
mybatis概述


環境搭配--前期準備
- 數據庫和實驗用表的準備
CREATE DATABASE mybatis1;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用戶名稱',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性別',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`)
VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),
(42,'小二王','2018-03-02 15:09:37','女','北京金燕龍'),
(43,'小二王','2018-03-04 11:34:34','女','北京金燕龍'),
(45,'傳智播客','2018-03-04 12:04:06','男','北京金燕龍'),
(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小馬寶莉','2018-03-08 11:44:00','女','北京修正');
SELECT * FROM USER
-
環境搭建的步驟
![]()
-
可能需要用到的依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>day1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging><!--打包方法-->
<!--依賴-->
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</version>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!--測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>


- domain
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
/*user表對應的實體類*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
/**
* 獲取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 設置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 設置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 獲取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 設置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 獲取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 設置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "User{id = " + id + ", username = " + username + ", birthday = " + birthday + ", sex = " + sex + ", address = " + address + "}";
}
}
- mybatis主配置文件
<?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>
<!--配置環境-->
<environments default="mysql">
<environment id="mysql">
<!--配置事務類型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置連接池(數據源)-->
<dataSource type="POOLED">
<!--配置連接池的信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件-->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
- userDao映射配置文件
<?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.itheima.dao.IUserDao">
<!--配置查詢所有-->
<select id="findAll">
select * from user
</select>
</mapper>
主配置文件和映射配置文件的約束在資料里面有
環境搭配的注意事項


mybatis的入門
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest {
public static void main(String[] args) throws Exception{
//1.讀取配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.創建SqlSessionFactory工廠
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工廠生產sqlSession對象
SqlSession sqlSession = factory.openSession();
//4.使用sqlSession創建dao接口的代理對象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5.使用代理對象執行方法
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6.釋放資源
sqlSession.close();
}
}
- 在mybatis
3.4.5中需要加上resultType,但是在mybatis 3.5.4就不用加resultType也可以識別出來
![]()
- 將會出現下面的異常
![]()
入門案例中的設計模式分析

mybatis注解開發和編寫dao實現類的方法
- 使用注解的方式實現前面的入門案例
![]()
![]()
寫dao的實現類也可以完成相同的功能,但是讓代碼過于繁雜,程序員無法將精力集中在業務中
![]()
![]()
3.自定義mybatis
執行查詢的所有分析

- 前提要求實體類屬性和表的列名一致
關注點1.如何創建代理對象以及使用的設計模式帶來的優勢2.這個過程中調用的組合關系
不必關注點:1.jdbc的執行2.xml的解析不需要去深究
![]()
為什么將映射文件中的全類名和SQL語句封裝成一個對象而不是參數?
如果封裝成參數,則有多條sql語句的時候不知道誰和誰是對應的(全類名和sql語句是一一對應的)
mybatis自定義編寫---根據測試類中缺少的創建類和接口
- 根據前面的分析進行編寫
首先我們pom.xml中將mybatis刪除掉
![]()
![]()
mybatis自定義編寫---解析xml的工具類的介紹
- 因為xml解析的原理不是很重要,我們使用工具類來完成
我們直接從資料中導入XMLconfigBuilder用于XML解析
![]()
![]()
![]()
此時已經基本理解XML對配置文件的解析操作。了解了映射關系
個人理解: - 1.讀取主配置文件
我們將讀取主配置文件的信息以流傳遞給xml解析器:1.先將主配置文件中連接數據庫所需要的屬性解析出來,并將其的值設置給Configuration配置類中的屬性2.根據映射文件的位置找到映射文件(然后開始解析映射文件)3.得到映射文件中所有的mapper標簽(一個mapper代表這個dao的一個方法)4.獲取映射文件的屬性(映射文件的路徑)傳遞給loadMapperConfiguration())函數將映射關系封裝成一個MAp集合5 將返回的map對象追加到Configuration配置類的Map集合(專門記錄映射關系)中. loadMapperConfiguration(String mapperPath)方法--專門封裝映射關系(解析映射文件)
1.方法中傳入了映射文件的路徑2.創建map集合Map<String,Mapper> mappers3.in = Resources.getResourceAsStream(mapperPath)獲取文件中的數據到流中4.將流中的數據傳入解析器開始解析5.獲取namespace(dao全限定列名)作為key的一部分6.獲取所有的select標簽7.循環遍歷select標簽,獲取id值(方法名)和namespace一起作為key名。得到resultType值和select語句,他們被一起封裝成Mapper對象,并將它們作為value值8.將key和value一起封裝成mapper對象,并返回
自定義mybatis編碼--創建2個默認實現類并分析類之間的操作
實現基于xml查詢所有的操作
對mybatis執行過程的概述
本質上只是干了2件事:1.解析XML文件2.創建代理對象
我們將XML文件的信息傳入解析器后,解析器將進行解析,得到連接信息(driver,username,password,url)和映射信息(被代理類全類名+方法名和增強的方法)(select語句+返回類型))并將他們封裝在Configuration類中
1.根據連接信息將會注冊驅動并獲取連接對象,并創建預處理對象2.session.getMapper(IUserDao.class);將會創建代理對象3.當調用相應的方法時,將通過映射關系查找出要執行的增強后的方法并執行
對mybatis中代理模式的理解
- 1.mybatis代理模式來執行sql語句,即在動態生成的代理方法中只是執行SQL,而沒有原始方法(沒有原始的被代理類)
- 2.我們的映射關系,可以理解為AOP中的切入點方法和通知方法相互綁定。和代理類中增強部分方法調用原始方法一樣,所以我們的映射關系可以抽象成代理類
(如果我知道動態生成的代理類的結構,也會是這樣的結構) - 3.既然我們生成的代理類就是一個映射,所以我們也可以說我們的dao是一個映射mapper
Mybatis的CRUD
- 學習內容
![]()
回顧自定義mybatis的流程分析

基于注解的自定義再分析



回顧環境搭配---實現查詢所有功能
- 配置文件的約束都是直接引入的

- dao
package com.athuima.dao;
import com.athuima.domain.User;
import java.util.List;
public interface UserDao {
List<User>findAll();
}
- 主配置文件
<?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>
<environments default="mysql"><!--環境名稱-->
<environment id="mysql">
<!--事務類型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置連接池-->
<dataSource type="POOLED">
<!--配置連接的信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>
</dataSource>
</environment>
</environments>
<!--配置映射文件的位置-->
<mappers>
<mapper resource="com/athuima/dao/userDao.xml"></mapper>
</mappers>
</configuration>
- 映射配置文件
<?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.athuima.dao.UserDao">
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
</mapper>
- 測試類
@Test
public void test() throws Exception{
//1.讀取主配置文件數據
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.創建工廠
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工廠生成數據庫操作對象sqlSession
final SqlSession sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理對象
final UserDao userDao = sqlSession.getMapper(UserDao.class);
//5.使用代理對象執行方法
final List<User> list = userDao.findAll();
//6.遍歷結果集
for (User user : list) {
System.out.println(user);
}
}
注意:當我們切換成使用注解時,一定要將原來目錄下的映射文件清除或者是換一個位置
保存操作
我們按照xml配置來實現
- userDao.xml中
<mapper namespace="com.athuima.dao.UserDao">
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
<insert id="save" parameterType="com.athuima.domain.User">/*傳入sql語句參數的類型*/
insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
</insert>
</mapper>
- 主配置文件
<!--配置映射文件的位置-->
<mappers>
<!--使用xml配置-->
<mapper resource="com/athuima/dao/userDao.xml"></mapper>
<!--使用注解-->
<!-- <mapper class="com.athuima.dao.UserDao"></mapper>-->
</mappers>
- dao
public interface UserDao {
/* @Select("select * from user")*/
List<User>findAll();
int save(User user);//保存方法
}
- 測試方法
package com.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.athuima.dao.UserDao;
import com.athuima.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
//抽取關閉資源的方法
@After//在測試方法之后執行
public void close() throws Exception {
//提交事務
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在測試方法之前執行
//抽取初始化方法
public void init() throws Exception{
//1.讀取主配置文件數據
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.創建工廠
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工廠生成數據庫操作對象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理對象
userDao = sqlSession.getMapper(UserDao.class);
}
@Test
public void testSelect() throws Exception{
//5.使用代理對象執行方法
final List<User> list = userDao.findAll();
//6.遍歷結果集
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testSave() throws Exception{
User user = new User();
user.setUsername("石文濤");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("北京");
//5.使用代理對象執行方法
final int row = userDao.save(user);
System.out.println(row);
}
}


當我們執行sql后,沒有報錯,但是數據的插入沒有成功

我們需要在操作的最后進行手動事務提交
修改和刪除操作
- 映射文件
<!--更新用戶-->
<update id="updateUser" parameterType="com.athuima.domain.User">
update user set username=#{username},address=#{address},sex=#{sex} ,birthday=#{birthday} where id=#{id};
</update>
<!--刪除用戶-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{dswwwxsssss}
</delete>
- dao
int updateUser(User user);
int deleteUser(Integer id);
- 測試
@Test
public void testUpdateUser() throws Exception{
User user = new User();
user.setId(53);
user.setSex("女");
user.setUsername("火云邪神");
user.setAddress("湯成一片");
userDao.updateUser(user);
}
@Test
public void testDeleteUser() throws Exception{
userDao.deleteUser(51);
}

其他的操作和前面的一樣
查詢一個和模糊查詢
- 映射文件
![]()
- dao
//查詢一個用戶
User findById(Integer id);
/*模糊查詢*/
List<User> findByUsername(String username);
- 測試
@Test
public void testSelectOne() throws Exception{
final User user = userDao.findById(51);
System.out.println(user);
}
@Test
public void testFindByUsername() throws Exception{
final List<User> users = userDao.findByUsername("%王%");
for (User user : users) {
System.out.println(user);
}
}
查詢返回單值操作
- 映射文件
<!--查詢總數據的條數-->
<select id="findTotalCount" parameterType="int">
select count(*) from user;
</select>
-
dao
int findTotalCount(); -
測試
@Test
public void testFindTotalCount() throws Exception{
final int count = userDao.findTotalCount();
System.out.println(count);
}
2種模糊查詢寫法細節分析


保存操作的細節---獲取保存數據的id
** SELECT LAST_INSERT_ID();這個sqk語句可以查詢出最后保存的id號,需要在你插入操作之后歷即調用 **
- 映射文件
<!--插入用戶-->
<insert id="save" parameterType="com.athuima.domain.User">/*傳入sql語句參數的類型*/
<!--配置插入操作之后,獲取插入數據的Id-->
<!--order:表明在最后一次插入操作之后執行-->
<!--keyColumn:對應的列,keyProperty:對應的屬性-->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (username,sex,birthday,address)values (#{username},#{sex},#{birthday},#{address});
</insert>
- 測試
@Test
public void testSave() throws Exception{
User user = new User();
user.setUsername("張三");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("日本");
System.out.println("插入之前:"+user);//打印出保存之前的user
//5.使用代理對象執行方法
final int row = userDao.save(user);
System.out.println("插入之后:"+user);//打印出保存之后的user
}

mybatis中的參數深入---使用實體類的包裝對象作為查詢條件
- 前面查詢條件的回顧
![]()
![]()
當我的查詢條件有多個對象時,我們可以將多個對象包裝成一個domain對象作為查詢條件

- 映射文件
<!--使用實體類的包裝類作為查詢條件-->
<select id="findByQueryVo" parameterType="com.athuima.domain.QueryVo">
select * from user where username like #{user.username}
</select>
- 查詢條件包裝類--domain
package com.athuima.domain;
//查詢條件的包裝類
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
- dao接口
/根據 QueryVo 中的條件模糊查詢用戶
List<User> findByQueryVo(QueryVo vo);
- 測試
@Test
public void testFindByQueryVo() throws Exception{
QueryVo vo= new QueryVo();
User user = new User();
user.setUsername("王");
vo.setUser(user);
final List<User> users = userDao.findByUsername("王");
for (User u : users) {
System.out.println(u);
}
}
mybatis中返回值的深入----調整實體類的屬性解決增和改方法的報錯
我們前面在查詢的時候要求我們的實體類和數據庫的列名一致,但是如果我們不一致會怎樣呢

- 對于非查詢操作來說
![]()
此時對于非查詢的報錯是可預料到的,我們將映射文件中的屬性值改成新的屬性值,就可以運行了 - 但是在查詢中呢
<!--查詢所有-->
<select id="findAll" resultType="com.athuima.domain.User">
select * from user;
</select>
@Test
public void testSelect() throws Exception{
//5.使用代理對象執行方法
final List<User> list = userDao.findAll();
//6.遍歷結果集
for (User user : list) {
System.out.println(user);
}
}

- 原因分析
![]()
我們的屬性用戶名只是改變了大小寫其他的并沒有改變,而列名在window系統下不區分大小寫所有username可以封裝成功
mybatis中返回值深入----解決實體類屬性和數據庫列名不對應的2種方式
我們封裝不成功的原因就是屬性名和列名匹配不上,我們需要解決的就是讓他們匹配上就可以了
- 1.第一種方法---起別名
我可以可以在執行sql語句的時候起別名,讓查詢出來的列名和屬性名一致 - 查詢所有的映射文件
<!--查詢所有-->
<select id="findAll" resultType="com.athuima.domain.User">
select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;
</select>
- 查詢結果
![]()
發現此時可以封裝成功了。這是從sql語句的層面上解決問題,執行的效率是最高的
-
2.方式2:在映射文件中配置實體類屬性和數據庫列名的對應關系
-
映射文件中的配置
<!--配置查詢結果的列名和實體類中的屬性名的映射關系-->
<resultMap id="usermap" type="com.athuima.domain.User">
<!--主鍵字段的映射-->
<id property="userId" column="id"></id>
<!--非主鍵字段的對應-->
<result property="userName" column="username"></result>
<result property="userBirthDay" column="birthday"></result>
<result property="userSex" column="sex"></result>
<result property="useraddRess" column="address"></result>
</resultMap>
<!--查詢所有-->
<select id="findAll" resultMap="usermap">
<!-- select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as useraddRess from user;-->
select * from user;
</select>
注意此時我們需要將返回的結果resultType改成resultMap表示使用結果映射
- 這2種方式的說明
直接在sql語句中修改添加別名,這種效率是最高的,但是這種方法的開發效率比較低,每條查詢語句的都需要添加別名。方法2,我們添加結果映射,這將帶來多解析一段xml文件,執行效率比較低,但是開發效率比較高
Mybatis 實現 DAO 的傳統開發方式(手動寫dao的實現類)
mybatis編寫dao的實現類---查詢列表
- dao接口
package com.atheima.dao;
import com.atheima.domain.User;
import java.util.List;
public interface UserDao {
/*查詢所有*/
List<User> findAll();
}
- dao實現類
package com.atheima.dao.impl;
import com.atheima.dao.UserDao;
import com.atheima.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
//UserDao的實現類
public class UserDaoImpl implements UserDao {
//定義工廠用于創建sqlSession對象
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public List<User> findAll() {
//工廠創建sqlSession對象
final SqlSession sqlSession = sqlSessionFactory.openSession();
final List<User> users = sqlSession.selectList("com.atheima.dao.UserDao.findAll");//參數是映射的key
sqlSession.close();
return users;
}
}
- 映射文件
<mapper namespace="com.atheima.dao.UserDao">
<select id="findAll" resultType="com.atheima.domain.User">
select * from user;
</select>
著配置文件是一樣的
- 測試
public class MyBatisTest {
private InputStream in;
private SqlSessionFactoryBuilder builder;
@After
public void close() throws Exception{
in.close();
}
@Before
public void init() throws Exception{
//加載配置文件
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.獲取SqlSessionFactory
builder = new SqlSessionFactoryBuilder();
}
@Test
public void testFindAll() throws Exception{
//3.創建dao
UserDao userDao = new UserDaoImpl(builder.build(in));
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
從這個例子我們可以看出,如果我們使用代理模式動態創建出代理(dao的實現類),這樣可以屏蔽很多細節。比如說完全屏蔽了使用sqlSession對象調用selectList方法,這些都是在動態代理中自動完成的。我們從實現類的結構可以看出我們的一個映射(mapper)其實就是一個實現類,所有說我們的動態代理完全可以依據映射關系被動態的創建出來
mybatis編寫dao的實現類---保存操作
- 映射文件
<insert id="saveUser" parameterType="com.atheima.domain.User" >
insert into user (id,username,birthday,sex,address)values (#{id},#{username},#{birthday},
#{sex},#{address});
</insert>
- dao實現類
@Override
public int saveUser(User user) {
//1.創建session對象
final SqlSession sqlSession = sqlSessionFactory.openSession();
//2.執行sql
final int row = sqlSession.insert("com.atheima.dao.UserDao.saveUser", user);
//3.提交事務
sqlSession.commit();
//4.釋放資源
sqlSession.close();
return row;
}
-
dao接口
int saveUser(User user); -
測試類
@Test
public void testSaveUser() throws Exception{
//3.創建dao
UserDao userDao = new UserDaoImpl(builder.build(in));
User user = new User();
user.setUsername("啥問題");
user.setAddress("曹縣");
user.setBirthday(new Date());
user.setSex("男");
userDao.saveUser(user);
}

mybatis編寫dao的實現類---修改刪除等其他操作
這些操作和前面的都差不了多少,可以查看IDEA中的代碼
mybatis編寫dao的實現類的執行過程分析---查詢方法
- 全套代碼分析流程看--截圖
![]()
![]()
我們啟動debug模式對finfAll()方法的代碼去向溯源,我們可以發現mybatis只是對jdbc進行了一層一層封裝,sql語句最終的執行還是由jdbc的預處理對象preperedStatement.executer()方法完成的
PreparedStatementHandler
從執行流程可以看出來,不管是增刪改操作最后都匯聚到執行PreparedStatementHandler類中的update方法,并且由update方法中的preparedStatement中的execute方法具體執行

mybatis中使用代理dao執行流程的分析


- 使用動態代理dao執行流程的全部分析
![]()
![]()
properties標簽的使用和細節(將連接信息放在外部屬性文件中,然后引入)

- 外部配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=888888
- 著配置文件
<?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>
<!--
配置properties標簽
可以在標簽內配置連接數據庫的信息。也可以通過屬性引用外部配置的屬性文件
resource屬性:
常用于指定配置文件的位置,按照類路徑的寫法來寫,且必須存在于類路徑下
-->
<!--r-->
<properties url="file:D:\ideaprojects\trationmybattis\src\main\resources/jdbcConfig.properties" >
<!--也可以使用使用類路徑 resource=jdbc.properties-->
<!--配置連接的信息-->
<!--我們可以在這里寫,然后再下面進行引用,但是寫在配置文件中然后引用更常見-->
<!--<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="888888"/>-->
</properties>
<!--mysql環境的配置-->
<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>
<!--配置映射文件的位置-->
<mappers>
<!--使用xml配置文件-->
<!--我們的mapper也可以使用url屬性定位映射文件的位置,前面的道理相同-->
<mapper resource="com/itheima/dao/userDao.xml"></mapper>
</mappers>
</configuration>
typeAliases標簽和package標簽

- 所以我們其實也可以給實體類起別名
- 注意:是在主配置文件中指定的
![]()
![]()
mybatis第三天
第1章 Mybatis 連接池與事務深入
內容介紹

連接池介紹
連接池就像一個容器,可以將我們的連接初始化放在一個容器里面,我們要用的時候只需要到容器中取就可以了


當取出一個后剩下的將按照隊列一樣位置全部往前縮進一位
mybatis連接池的介紹
在傳統的JDBC中中我們獲取連接由DeiverManager接口負責,但是使用DeiverManager來獲取連接效率太低了。現在一般使用連接池來管理連接。java提供了DataSourse接口來管理連接池的連接

mybatis中采用unpooled配置連接池的原理分析



通過查看UNPOOLED模型的實現類UnPooledDataSource中關于獲取連接的方法,我們可以知道,每次獲取連接都需要注冊驅動,獲取連接,最后是否連接。沒有使用到連接池
mybatis中采用pooled配置連接池的原理分析


總體思路就是:見上圖
mybatis中的事務原理和自動提交設置



所以其實我們是可以自己手動設置是否自動提交的,如果不設置則默認是關閉自動提交
- 在創建sqlSession對象的時候可以設置時候自動提交
![]()
第二章mybatis映射文件的SQL深入
這個一般是針對查詢語句的,當我們的查詢條件不同,將不同的查詢結果
mybatis中的動態sql語句--if標簽
只能使用 and不能使用&&

- 映射文件
![]()
- 測試
//4.根據傳入的條件動態查詢
@Test
public void testFindByCondition() throws Exception{
User user = new User();
user.setUserName("老王");
// user.setUseraddRess("北京");//也可用于地址
final List<User> users = userDao.findByCondition(user);
for (User u : users) {
System.out.println(u);
}
}
mybatis中的動態sql語句--where標簽的使用
我們可以使用where標簽來替代where1=1這個條件

mybatis中的動態sql語句--foreach和sql標簽

我們需要解決的是如何將傳入的參數集合賦值給sql語句中in()這個集合中

- 映射文件
<select id="findUserInIds" parameterType="com.athuima.domain.QueryVo" resultType="com.athuima.domain.User">
select * from user
<where>
<if test="ids!=null and ids.size()>0">
<foreach collection="ids" open=" and id in(" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
- 測試
//根據queryVo中的id集合實現查詢用戶列表
@Test
public void findUserInIds() throws Exception{
QueryVo vo= new QueryVo();
List<Integer> ids = new ArrayList<>();
ids.add(41);
ids.add(42);
ids.add(43);
vo.setIds(ids);
final List<User> users = userDao.findUserInIds(vo);
for (User u : users) {
System.out.println(u);
}
}
- sql標簽--抽取重復的ql語句
![]()

第三章mysql多表關聯查詢
mybatis的多表關聯查詢

完成account表的建立及實現單表查詢

建立用戶表和賬戶表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用戶名稱',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性別',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龍'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龍'),(45,'傳智播客','2018-03-04 12:04:06','男','北京金燕龍'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小馬寶莉','2018-03-08 11:44:00','女','北京修正');
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` INT(11) NOT NULL COMMENT '編號',
`UID` INT(11) DEFAULT NULL COMMENT '用戶編號',
`MONEY` DOUBLE DEFAULT NULL COMMENT '金額',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)-- 和用戶表的用戶編號綁定外鍵
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `account`(`ID`,`UID`,`MONEY`) VALUES (1,46,1000),(2,45,1000),(3,46,2000);
完成查詢所有賬戶表
- 主配置文件中指定映射文件的位置
<mapper resource="com/athuima/dao/accountDao.xml"></mapper> - 映射配置文件
<mapper namespace="com.athuima.dao.AccountDao">
<!--1.查詢所有-->
<select id="findAll" resultType="com.athuima.domain.Account">
select * from account;
</select>
</mapper>
- dao接口
/**
* 查詢所有賬戶
* @return
*/
List<Account>findAll();
- 測試
@Test
public void testSelect(){
final List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
完成account一對一操作--通過寫account子類方式查詢
但是現在我們的要求就是查詢所有賬戶,同時還要獲得當前賬戶所屬用戶的信息,其實就是實現多表查詢
這個問題本質上是在進行多表查詢的時候,怎樣對查詢結果進行封裝的問題
- 我們現在需要取出賬戶表的全部信息,已經每個賬戶對應的用戶的姓名和地址信息
我們通過創建子類的方法開拓展實體類,使得其可以封裝多表查詢的結果,但是這種方法現在并不多見 - 實體類(繼承了Account類)
package com.athuima.domain;
public class AccountUser extends Account{
private String username;
private String address;
public AccountUser() {
}
public AccountUser(String username, String address) {
this.username = username;
this.address = address;
}
/**
* 獲取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 設置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return super.toString()+ "AccountUser{username = " + username + ", address = " + address + "}";
}
}
- 映射文件
<!--查詢所有賬戶同時包含用戶名和地址信息-->
<select id="findAllAccountUser" resultType="com.athuima.domain.AccountUser">
select a.*,u.username,u.address
from user u join account a on u.id=a.uid;
</select>
- 測試
@Test
public void findAllAccountUser(){
final List<AccountUser> accounts = accountDao.findAllAccountUser();
for (Account account : accounts) {
System.out.println(account);
}
}
完成account一對一操作--建立實體類關系的方式
我們的每個訂單它屬于一個用戶,在表中我們是根據在訂單表中設置外鍵來表示的。在實體類中我們可以在訂單表中放置用戶的引用來表示這一點
我們的表和實體類會根據屬性和列進行自動封裝(我們要指定封裝的類型),但是當我們在account中添加user對象,這個對象就不能封裝可進行了。所以我們必須要為這個查詢專門設計resultMap
- account類(關聯一個user對象,實現一對一關系)
package com.athuima.domain;
import java.io.Serializable;
//賬戶表對應的實體類
public class Account implements Serializable {
private Integer id;//賬戶id
private Integer uid;//用戶id
private Double money;//賬戶余額
private User user;//一個賬戶屬于一個用戶
public Account() {
}
public Account(Integer id, Integer uid, Double money, User user) {
this.id = id;
this.uid = uid;
this.money = money;
this.user = user;
}
/**
* 獲取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 設置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取
* @return uid
*/
public Integer getUid() {
return uid;
}
/**
* 設置
* @param uid
*/
public void setUid(Integer uid) {
this.uid = uid;
}
/**
* 獲取
* @return money
*/
public Double getMoney() {
return money;
}
/**
* 設置
* @param money
*/
public void setMoney(Double money) {
this.money = money;
}
/**
* 獲取
* @return user
*/
public User getUser() {
return user;
}
/**
* 設置
* @param user
*/
public void setUser(User user) {
this.user = user;
}
public String toString() {
return "Account{id = " + id + ", uid = " + uid + ", money = " + money + ", user = " + user + "}";
}
}
- 映射文件
<!--配置結果映射關系-->
<resultMap id="accountmap" type="com.athuima.domain.Account">
<!--主鍵-->
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--用于指定表方關聯的引用實體屬性-->
<association property="user" javaType="com.athuima.domain.User"><!--javaType用于指定關聯屬性的類型-->
<id property="id" column="id"></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="findAllAccountUser" resultMap="accountmap" >
select a.*,u.username,u.address
from user u join account a on u.id=a.uid;
</select>
- 測試
@Test
public void findAllAccountUser(){
final List<AccountUser> accounts = accountDao.findAllAccountUser();
for (Account account : accounts) {
System.out.println(account);
}
}

一對多查詢操作
要求查詢出所有用戶和他們對應的賬戶信息。一個用戶可能對應一對多的關系,我們需要在實體類中實現這個關系
- 映射文件
<!--建立user表的結果映射-->
<resultMap type="com.athuima.domain.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="com.athuima.domain.Account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!--1.查詢所有-->
<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>
說明:resultMap的格式(特別是屬性的位置還是按照上面的格式寫會比較號好),因為可能會出現無法將多個賬戶封裝到一個集合的情況
- user(和賬戶關聯一對多的關系)
package com.athuima.domain;
import java.util.Date;
import java.util.List;
/**
* @author SWT
* @date 2023/12/09
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//一個用戶可能對應多個賬戶
private List<Account> accounts;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address, List<Account> accounts) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
this.accounts = accounts;
}
/**
* 獲取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 設置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 設置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 獲取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 設置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 獲取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 設置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
/**
* 獲取
* @return accounts
*/
public List<Account> getAccounts() {
return accounts;
}
/**
* 設置
* @param accounts
*/
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public String toString() {
return "User{id = " + id + ", username = " + username + ", birthday = " + birthday + ", sex = " + sex + ", address = " + address + ", accounts = " + accounts + "}";
}
}
- 測試
@Test
public void testSelect(){
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
- 直接執行sql語句的情況
![]()
![]()
分析mybatis的多對多的步驟并搭建環境
- 角色即身份的意思
![]()
![]()
多對多--準備角色表的實體類和映射配置
- 添加role表并插入數據
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`ID` INT(11) NOT NULL COMMENT '編號',
`ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名稱',
`ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) VALUES (1,'院長','管理整個學院'),(2,'總裁','管理整個公司'),(3,'校長','管理整個學校');
- 插入中間表--- user--role
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`UID` INT(11) NOT NULL COMMENT '用戶編號',
`RID` INT(11) NOT NULL COMMENT '角色編號',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user_role`(`UID`,`RID`) VALUES (41,1),(45,1),(41,2);
查詢角色獲取角色下屬用戶信息
多對多是一種表的關系的體現,多對多可以看成是2個一對多,且中間表的列和其他2個表存在外鍵約束,

因為要查詢出來所有用戶所以我們需要左外連

- 映射文件
<!--定義 role 表的 ResultMap-->
<resultMap id="roleMap" type="com.athuima.domain.Role">
<id property="roleId" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="com.athuima.domain.User">
<id column="uid" 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
r.*,u.id uid,
u.username username,
u.birthday birthday,
u.sex sex,
u.address address
FROM
ROLE r
INNER JOIN
USER_ROLE ur
ON ( r.id = ur.rid)
INNER JOIN
USER u
ON (ur.uid = u.id);
</select>
- role類
```java
package com.athuima.domain;
import java.io.Serializable;
import java.util.List;
//角色表對應的實體類
public class Role implements Serializable {
private Integer roleId;//角色id
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 Role() {
}
public Role(Integer roleId, String roleName, String roleDesc) {
this.roleId = roleId;
this.roleName = roleName;
this.roleDesc = roleDesc;
}
/**
* 獲取
* @return roleId
*/
public Integer getRoleId() {
return roleId;
}
/**
* 設置
* @param roleId
*/
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
/**
* 獲取
* @return roleName
*/
public String getRoleName() {
return roleName;
}
/**
* 設置
* @param roleName
*/
public void setRoleName(String roleName) {
this.roleName = roleName;
}
/**
* 獲取
* @return roleDesc
*/
public String getRoleDesc() {
return roleDesc;
}
/**
* 設置
* @param roleDesc
*/
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
", users=" + users +
'}';
}
}
- 測試
public void testSelect() throws Exception{
//5.使用代理對象執行方法
final List<Role> roles = roleDao.findAll();
//6.遍歷結果集
for (Role role : roles) {
System.out.println(role);
}
}

注意看結果體現了多對多的關系
查詢用戶--獲取用戶所包含的角色信息
我們的sql語句如果強調使用左右連接可能需要改,其他情況和前面相比,我們的sql都不用修改。我們只需要在user類中添加多對多關系
- user類
package com.athuima.domain;
import java.util.Date;
import java.util.List;
/**
* @author SWT
* @date 2023/12/09
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//多對多關系 :一個用戶有多個角色
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
/**
* 獲取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 設置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取
* @return username
*/
public String getUsername() {
return username;
}
/**
* 設置
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 獲取
* @return birthday
*/
public Date getBirthday() {
return birthday;
}
/**
* 設置
* @param birthday
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/**
* 獲取
* @return sex
*/
public String getSex() {
return sex;
}
/**
* 設置
* @param sex
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
/**
* 獲取
* @return accounts
*/
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", roles=" + roles +
'}';
}
}
- 映射文件
<!--建立user表的結果映射-->
<resultMap type="com.athuima.domain.User" id="userMap">
<id column="uid" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<collection property="roles" ofType="com.athuima.domain.Role">
<id property="roleId" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!--1.查詢所有用戶,并且查出用戶所有的角色信息-->
<select id="findAll" resultMap="userMap" >
SELECT
r.*,u.id uid,
u.username username,
u.birthday birthday,
u.sex sex,
u.address address
FROM
ROLE r
INNER JOIN
USER_ROLE ur
ON ( r.id = ur.rid)
INNER JOIN
USER u
ON (ur.uid = u.id);
</select>

補充內容--JNDI
補充--JDNI的概述和原理
- JNDI模仿windows的注冊表

補充--JDNI搭建maven的war工程



補充--測試JNDI數據源的使用以及以及使用細節
原理還不是很懂,好像這個用的也不多
第四天
今日課程安排

延遲加載和立即加載的概念

我們已查詢用戶就會把該用戶100個賬戶都查詢出來,這對內存無疑是巨大的開銷

應該需要的是當我們不需要使用賬戶的時候就應該把賬戶查詢出來,但是又帶來一個問題,那我們需要用的時候不就使用不了了嗎
mybatis一對一實現延遲加載
在實際開發中一對一和多對一應該是使用的延遲加載,這里只是舉一個例子
- 查詢賬戶,實現延遲加載用戶
![]()
![]()
延遲加載根據用到才會去加載,才會查詢數據庫獲取數據,如果沒有用到關聯的數據,不會去查詢數據庫
- 主配置文件中:配置mybatis延遲加載
<settings>
<!--開啟mybatis延遲加載-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--允許觸發方法進行立即加載 ,否則按需加載-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- 映射配置文件
<!--配置結果映射關系-->
<resultMap id="accountmap" type="com.athuima.domain.Account">
<!--主鍵-->
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--用于指定表方關聯的引用實體屬性-->
<!--select指定的內容:查詢用戶的唯一標識-->
<!--column屬性指定的內容:用戶根據id查詢時,所需要參數的值-->
<association property="user" column="uid" javaType="com.athuima.domain.User" select="com.athuima.dao.UserDao.findById"><!--javaType用于指定關聯屬性的類型-->
<!--按照默認封裝-->
</association>
</resultMap>
<!--1.查詢所有-->
<select id="findAll" resultMap="accountmap">
select * from account;
</select>
- 賬戶類
package com.athuima.domain;
import java.io.Serializable;
//賬戶表對應的實體類
public class Account implements Serializable {
private Integer id;//賬戶id
private Integer uid;//用戶id
private Double money;//賬戶余額
private User user;//一個賬戶屬于一個用戶
public Account() {
}
public Account(Integer id, Integer uid, Double money, User user) {
this.id = id;
this.uid = uid;
this.money = money;
this.user = user;
}
/**
* 獲取
* @return id
*/
public Integer getId() {
return id;
}
/**
* 設置
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取
* @return uid
*/
public Integer getUid() {
return uid;
}
/**
* 設置
* @param uid
*/
public void setUid(Integer uid) {
this.uid = uid;
}
/**
* 獲取
* @return money
*/
public Double getMoney() {
return money;
}
/**
* 設置
* @param money
*/
public void setMoney(Double money) {
this.money = money;
}
/**
* 獲取
* @return user
*/
public User getUser() {
return user;
}
/**
* 設置
* @param user
*/
public void setUser(User user) {
this.user = user;
}
public String toString() {
return "Account{id = " + id + ", uid = " + uid + ", money = " + money ;
}
}
- 測試類1





mybatis一對多實現延遲加載
思想是:在需要使用的時候去調用對方配置文件中的配置來實現查詢的功能
- 配置文件
<resultMap type="com.athuima.domain.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" column="id" ofType="com.athuima.domain.Account" select="com.athuima.dao.AccountDao.findAccountsByUid">
</collection>
</resultMap>
<!--1.查詢所有-->
<select id="findAll" resultMap="userMap" >
select * from user;
</select>
- 賬戶dao映射配置文件
<!--1.根據用戶id查詢賬戶-->
<select id="findAccountsByUid" resultType="com.athuima.domain.Account">
select * from account where uid=#{id}
</select>
測試部分和前面的一樣
緩存的概念

適用于緩存的舉例:淘寶中商品的庫存數,可能真實的數據庫中名沒有商品了,但是緩存中顯示還有商品,但是造成的后果影響不大
mybatis中的一級緩存

當我們查詢的是同樣的結果,將直接在sqlSession的緩存中提取,而不會到數據庫中進行查詢


說明我們第二次查詢并沒有到數據庫中查詢,而是到sqlSession的緩存中取出的結果,返回了相同的user對象


觸發清空一級緩存的情況
使用緩存,緩存和數據庫的數據同步是我們需要關注的一個問題
- 我們在2個查詢相同內容的中間,使用update方法進行修改,第二次查詢的內容將會是怎樣的呢*
![]()
![]()
![]()
![]()
- 只要是update insert delete操作都更新,不需要和查詢的內容相同
![]()
mybates的二級緩存

- 二級緩存原理圖
![]()
![]()
![]()
二級緩存的配置






在第二次查詢的時候,會創建一個新的user對象,會將二級緩存中的數據填充到新創建的對象里面去。雖然沒有發起查詢,但是卻重寫創建了一個對象。所有2個user地下并不相等
mybatis注解開發

手動搭建環境
mybatis注解開發測試和使用注意事項

- 注意事項
![]()
但是此時同樣會報錯。也就是注解和使用xml文件,二者不能同時存在
結論:只要你使用注解開發,但是在你的配置文件路徑下同時包含了映射配置文件,此時不管你用不用這個映射配置文件,他都會報錯(mybatis內部設置的)
mybatis注解開發保存和更新功能
- IUserDao接口
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface IUserDao {
/**
* 查詢所有
* @return
*/
@Select("select * from user" )
List<User>findAll();
/*保存操作*/
@Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
void save(User user);
//更新操作
@Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
void update(User user);
}
- 著配置文件
<?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>
<!--引入外部配置文件-->
<properties resource="jdbcConfig.properties"></properties>
<!--將該包里面所有的類都注冊別名-->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!--JDBC環境配置-->
<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>
<mappers>
<!--指定該包下面的所有dao接口的映射所在的位置-->
<package name="com.itheima.dao"/>
</mappers>
</configuration>
- 測試類
package com.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/*
*Account表的測試
* */
public class AnnotationCRUDTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
//抽取關閉資源的方法
@After//在測試方法之后執行
public void close() throws Exception {
//提交事務
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在測試方法之前執行
//抽取初始化方法
public void init() throws Exception{
//1.讀取主配置文件數據
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.創建工廠
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
//3.利用工廠生成數據庫操作對象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理對象
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void testSelect(){
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testUpdate(){
User user = new User();
user.setId(577);
user.setAddress("北京");
user.setBirthday(new Date());
user.setUsername("張三");
user.setSex("女");
userDao.update(user);
}
@Test
public void testInsert(){
User user = new User();
user.setId(577);
user.setAddress("湖北省十堰市");
user.setBirthday(new Date());
user.setUsername("黑馬程序員");
user.setSex("男");
userDao.save(user);
}
}
mybatis注解開發CRUD的其他操作
- dao
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface IUserDao {
/**
* 查詢所有
* @return
*/
@Select("select * from user" )
List<User>findAll();
/*保存操作*/
@Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
void save(User user);
//更新操作
@Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
void update(User user);
//刪除
@Delete("delete from user where id=#{id}")
void deleteUser(Integer id);
//查詢單個對象
@Select("select * from user where id = #{id}")
User selectOneUser(Integer id);
//對用戶名進行模糊查詢
@Select("select * from user where username like #{username}")
List<User>selectByUsername(String username);
//返回表中記錄條數
@Select("select count(*) from user")
int findTotal();
}
- 測試
package com.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/*
*Account表的測試
* */
public class AnnotationCRUDTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
//抽取關閉資源的方法
@After//在測試方法之后執行
public void close() throws Exception {
//提交事務
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before//在測試方法之前執行
//抽取初始化方法
public void init() throws Exception {
//1.讀取主配置文件數據
in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.創建工廠
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.利用工廠生成數據庫操作對象sqlSession
sqlSession = sqlSessionFactory.openSession();
//4.使用sqlSession生成代理對象
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void testSelect() {
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testUpdate() {
User user = new User();
user.setId(577);
user.setAddress("北京");
user.setBirthday(new Date());
user.setUsername("張三");
user.setSex("女");
userDao.update(user);
}
@Test
public void testInsert() {
User user = new User();
user.setId(577);
user.setAddress("湖北省十堰市");
user.setBirthday(new Date());
user.setUsername("黑馬程序員");
user.setSex("男");
userDao.save(user);
}
@Test
public void testDelete() {
userDao.deleteUser(577);
}
@Test
public void testOneUser() {
final User user = userDao.selectOneUser(48);
System.out.println(user);
}
@Test
public void testSelectByUsername() {
final List<User> users = userDao.selectByUsername("%王%");
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testFindTotal() {
final int total = userDao.findTotal();
System.out.println(total);
}
}
mybatis注解建立實體類屬性和數據庫表中列的對應關系
- 配置resultMap結果映射(用于替代xml配置中resultMap標簽)
- user類(屬性名稱和數據庫列名不一致)
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public User() {
}
public User(Integer userId, String userName, Date userBirthday, String userSex, String userAddress) {
this.userId = userId;
this.userName = userName;
this.userBirthday = userBirthday;
this.userSex = userSex;
this.userAddress = userAddress;
}
/**
* 獲取
* @return userId
*/
public Integer getUserId() {
return userId;
}
/**
* 設置
* @param userId
*/
public void setUserId(Integer userId) {
this.userId = userId;
}
/**
* 獲取
* @return userName
*/
public String getUserName() {
return userName;
}
/**
* 設置
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* 獲取
* @return userBirthday
*/
public Date getUserBirthday() {
return userBirthday;
}
/**
* 設置
* @param userBirthday
*/
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
/**
* 獲取
* @return userSex
*/
public String getUserSex() {
return userSex;
}
/**
* 設置
* @param userSex
*/
public void setUserSex(String userSex) {
this.userSex = userSex;
}
/**
* 獲取
* @return userAddress
*/
public String getUserAddress() {
return userAddress;
}
/**
* 設置
* @param userAddress
*/
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String toString() {
return "User{userId = " + userId + ", userName = " + userName + ", userBirthday = " + userBirthday + ", userSex = " + userSex + ", userAddress = " + userAddress + "}";
}
}


package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface IUserDao {
/**
* 查詢所有
* @return
*/
@Select("select * from user" )
@Results( id = "usermap",
value = {@Result(id = true,property = "userId",column = "id"),//id用于標記是否為主鍵(默認為false)
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address")
}
)
List<User>findAll();
//查詢單個對象
@Select("select * from user where id = #{id}")
@ResultMap("usermap")//引用前面定義的結果映射
User selectOneUser(Integer id);
//對用戶名進行模糊查詢
@Select("select * from user where username like #{username}")
@ResultMap({"usermap","selectOneUser"})//引用前面定義的結果映射
List<User>selectByUsername(String username);
}
這樣我們的結果集封裝就沒問題了
mybatis注解開發一對一查詢配置
- 在這個里面同時設置是否延遲加載
![]()
- dao
public interface IAccountDao {
//查詢所有賬戶
@Select("select * from account")
@Results(id = "accountmap",value = {
//賬戶結果映射
@Result(id = true,property = "id",column = "id"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property ="user",column = "uid",one=@One(select = "com.itheima.dao.IUserDao.findById",fetchType = FetchType.EAGER ))//指定不延遲加載
}
)
List<Account> findAll();
- 測試
@Test
public void testSelect() {
final List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
System.out.println(account.getUser());
}
}

他們的配置原理和xml文件中完全一致
mybatis注解開發一對多查詢配置

- dao
@Select("select * from user" )
@Results( id = "usermap",
value = {@Result(id = true,property = "userId",column = "id"),//id用于標記是否為主鍵(默認為false)
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address"),
//根據用戶id查詢他的用戶
@Result(property = "accounts",column = "id",many = @Many(select = "com.itheima.dao.IAccountDao.findByUid",fetchType = FetchType.EAGER ))
}
)
List<User>findAll();

- 測試
@Test
public void testSelect() {
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
System.out.println("該用戶對應的賬戶是----------------------------------------------");
System.out.println(user.getAccounts());
System.out.println("-------------------------------------------------------------------------------");
}
}

mybatis注解開發使用二級緩存
一級緩存 自動開啟的


















































浙公網安備 33010602011771號