Mybatis學習
本文圖文學習教程
學自b站狂神:狂神說–Mybatis篇
1.簡介
1.1什麼是Mybatis
- MyBatis 是一款優秀的持久層框架
- 它支持自定義 SQL、存儲過程以及高級映射。
- MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。
- MyBatis 可以通過簡單的 XML 或註解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)爲數據庫中的記錄。
- MyBatis 本是apache的一個開源項目iBatis,
- 2010年這個項目由apache software foundation 遷移到了google code,並且改名爲MyBatis 。
- 2013年11月遷移到Github。
如何獲取Mybais:
- maven倉庫:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
-
中文文檔:https://mybatis.org/mybatis-3/zh/index.html
-
Github源碼:https://github.com/mybatis/mybatis-3/releases
1.2持久化
數據持久化:
- 持久化就是將程序在持久狀態和瞬時狀態轉化的過程
- 內存:斷電即失
- 即將數據從原來不“穩定”的內存中轉移到“穩定”的數據庫的過程
- 舉例:冷藏食物
爲什麼需要持久化:
- 防止丟失重要信息
- 內存太貴了,
- 存放在數據庫,即用即取更好
1.3持久層
Dao層、Service層、Controller層…
- 完成持久化工作的代碼塊
- 層是界限十分明顯
1.4爲什麼需要Mybatis?
-
幫助程序猿更加方便的將存入到數據庫中
-
傳統的JDBC代碼太複雜了。
-
框架更加簡化,更加自動化,易上手。
-
使用人羣多。
-
優點:
- 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。
- 靈活:mybatis不會對應用程序或者數據庫的現有設計強加任何影響。 sql寫在xml裏,便於統一管理和優化。通過sql語句可以滿足操作數據庫的所有需求。
- 解除sql與程序代碼的耦合:通過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。
- 提供映射標籤,支持對象與數據庫的orm字段關係映射
- 提供對象關係映射標籤,支持對象關係組建維護
- 提供xml標籤,支持編寫動態sql。
2.第一個Mybatis程序
思路:搭建環境–>導入Mybatis–>編寫代碼–>測試
2.1搭建環境
搭建數據庫:
CREATE DATABASE `mybatis`;
Use `mybatis`;
create table `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT into user VALUES(1,"劉一手","123");
INSERT into user VALUES(2,"劉二手","123"),(3,"劉三手","123"),(4,"劉四手","123");
INSERT into user(id,name) VALUES(5,"劉五手");
新建項目
-
新建一個普通的maven項目,注:固定Maven路徑
-
刪除src目錄,將項目作爲父工程
-
導入maven依賴
<?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>com.lxf</groupId>
<artifactId>20200604-Mybatis-study</artifactId>
<version>1.0-SNAPSHOT</version>
<!--導入依賴-->
<dependencies>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
2.2創建一個子模塊
- 編寫mybatis的核心配置文件
連接數據庫錯誤解決:https://blog.csdn.net/ITMan2017/article/details/100601438
<?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核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xxxx"/>
</dataSource>
</environment>
</environments>
<!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
<!--namespace中的包名要和Dao/mapper接口的包名一致!-->
<mappers>
<mapper resource="com/lxf/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
- 編寫mybatis工具類
package com.lxf.utils;
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 java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步:獲取sqlSessionFactory對象
InputStream inputStream = MybatisUtils.class.getResourceAsStream("/mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
// public static void main(String[] args) {
// System.out.println(sqlSessionFactory);
// }
//既然有了SqlSessionFactory,就可以從中獲取SqlSession的實例了。
//SqlSession 完全包含了面向數據庫執行SQL命令所需的所有方法。
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
2.3編寫代碼
- 實體類
package com.lxf.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- Dao接口
package com.lxf.dao;
import com.lxf.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserDao {
//查詢所有的User
List<User> getUserList();
//查詢單獨一個User
User getUser(int id);
}
- 接口實現類(由原來的UserDaoImpl–>Mapper配置文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<mapper namespace="com.lxf.dao.UserDao">
<!--select查詢語句-->
<select id="getUserList" resultType="com.lxf.pojo.User">
select * from mybatis.user;
</select>
</mapper>
2.4測試
出錯點:
1.org.apache.ibatis.binding.BindingException:Type interface com.lxf.dao.UserDao is not konwn to the MapperRegistry
:每一個Mapper.xml沒有在Mybatis核心配置文件中註冊!
2.路徑正確但是Mapper.xml文件資源無法找到:因爲maven項目約定大於配置,資源只會在resource文件夾下搜索,pom.xml文件加上以下配置就可以解決錯誤:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
測試類(Junit):
package com.lxf.dao;
import com.lxf.pojo.User;
import com.lxf.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession =null;
try{
//1.獲取session
sqlSession=MybatisUtils.getSqlSession();
//2.執行SQL
//方式一(推薦):
UserDao userDao = sqlSession.getMapper(UserDao.class);
System.out.println("userDao.getUserList() = " + userDao.getUserList());
//方式二:
User user = sqlSession.selectOne("getUser", 1);
System.out.println("user = " + user);
} catch (Exception e) {
e.printStackTrace();
}finally {
//關閉sqlSession
sqlSession.close();
}
}
}
2.5.增刪改查
1.修改UserMapper接口:
package com.lxf.dao;
import com.lxf.pojo.User;
import java.util.List;
public interface UserMapper {
//方式一:
//查詢所有的User,配置在mapper.xml文件中
List<User> getUserList();
//查詢單獨一個User,註解不需要配置xml文件
User getUserById(int id);
//增加一個用戶
int addUser(User user);
//增加多個用戶
int addUsers(List<User> users);
//修改一個用戶
int updateUser(User user);
//刪除一個用戶
int deleteUser(int id);
}
2.修改mapper.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<mapper namespace="com.lxf.dao.UserMapper">
<!--select查詢語句,查詢所有的User-->
<select id="getUserList" resultType="com.lxf.pojo.User">
select * from mybatis.user;
</select>
<!--select查詢語句,查詢id對應的User-->
<select id="getUserById" resultType="com.lxf.pojo.User" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
<!--insert插入語句,插入一個數據-->
<insert id="addUser" parameterType="com.lxf.pojo.User">
insert into mybatis.user values(#{id},#{name},#{pwd});
</insert>
<!--修改語句,修改一個數據-->
<update id="updateUser" parameterType="com.lxf.pojo.User" >
update User as u set u.pwd=#{pwd},u.name=#{name} where u.id=#{id};
</update>
<!--insert插入語句,插入多個數據-->
<insert id="addUsers" parameterType="java.util.List" useGeneratedKeys="true">
INSERT INTO User (id,name,pwd)
VALUES
<foreach collection="list" separator="," item="item">
(#{item.id,jdbcType=INTEGER},
#{item.name,jdbcType=VARCHAR},
#{item.pwd,jdbcType=VARCHAR})
</foreach>
</insert>
<!--delete刪除語句,刪除指定用戶-->
<delete id="deleteUser" parameterType="int">
delete from User where id=#{id};
</delete>
</mapper>
3.編寫測試類:
package com.lxf.dao;
import com.lxf.pojo.User;
import com.lxf.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession =null;
try{
//1.獲取session
sqlSession=MybatisUtils.getSqlSession();
//2.執行SQL
//方式一(推薦):
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("userDao.getUserList() = " + userMapper.getUserList());
//方式二:
//User user = sqlSession.selectOne("getUser", 1);
//System.out.println("user = " + user);
} catch (Exception e) {
e.printStackTrace();
}finally {
//關閉sqlSession
sqlSession.close();
}
}
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//根據id查詢一個用戶
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// User user = mapper.getUserById(1);
// System.out.println("user = " + user);
//增加一個用戶
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// int i=mapper.addUser(new User(9,"劉德華","234"));
// if(i>0){
// System.out.println("插入多條數據成功");
// }
//增加多個用戶
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList=new ArrayList<User>();
userList.add(new User(10,"張學友","zxy"));
userList.add(new User(11,"周潤發","zrf"));
userList.add(new User(12,"周星馳","zxc"));
int i = mapper.addUsers(userList);
if (i>0){
System.out.println("插入多條數據成功");
}
//修改一個用戶
//UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//mapper.updateUser(new User(8,"劉八手","888"));
//刪除指定的一個用戶
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// mapper.deleteUser(8);
//提交事務
sqlSession.commit();
sqlSession.close();
}
}
注意點:
-
增刪改需要提交事務
-
在增加多個數據時,url要加上:&allowMultiQueries=true
-
mapper文件中如果錯用標籤會發生截然不同的事情,例如:在select標籤下sql用增加語句,會導致查詢後返回爲null,且不需要提交事務。原因:select標籤下也可以用其他的查詢語句,但select標籤默認返回值爲null,且不需要提交事務。
2.6萬能Map
如果我們的實體類含有大量字段,但是在插入、修改的時候不需要全部填寫,就可以用這種方法省時省力
UserMapper接口:
//增加一個用戶,萬能Map方式
int addUser2(Map<String,Object> map);
UserMapper.xml配置文件
<!--insert插入語句,萬能Map法-->
<insert id="addUser2">
insert into mybatis.user(id,name) values(#{userId},#{userName});
</insert>
測試類:
@Test
public void addUser2(){
//獲取session對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//獲取UserMapper對象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map=new HashMap<String,Object>();
map.put("userId",14);
map.put("userName","王小法");
//執行方法
int i=userMapper.addUser2(map);
if(i>0){
System.out.println("插入成功!");
}
sqlSession.commit();
sqlSession.close();
}
2.7.模糊查詢:
1.傳統挺配符:% %
List<User> usersLike = userMapper.getUsersLike("%周%");
2.在sql拼接中使用通配符
select * from user where name like "%"#{value}"%";
3.注意
標籤中註釋的sql也會編譯,所以不用的sql就要刪了,註釋沒用,會報錯,一般就是參數數量不匹配。
3.配置解析
1.核心配置文件
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)(需要再瞭解)
objectFactory(對象工廠)(需要再瞭解)
plugins(插件)(需要再瞭解)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)(需要再瞭解)
mappers(映射器)(需要再瞭解)
2.環境配置(environments)
<!--default:默認的環境,寫哪個id就對應哪個環境-->
<environments default="test">
<environment id="development">
<!--transactionManager:事務管理,
type:[JDBC|MANAGED]
JDBC:– 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從數據源獲得的連接來管理事務作用域(默認)。
MANAGED:這個配置幾乎沒做什麼。它從不提交或回滾一個連接,而是讓容器來管理事務的整個生命週期(比如 JEE 應用服務器的上下文)。 默認情況下它會關閉連接。然而一些容器(例如EJB)並不希望連接被關閉,因此需要將 closeConnection 屬性設置爲 false 來阻止默認的關閉行爲。
-->
<transactionManager type="JDBC"/>
<!--
type=[UNPOOLED|POOLED|JNDI]
UNPOOLED:這個數據源的實現會每次請求時打開和關閉連接。雖然有點慢,但對那些數據庫連接可用性要求不高的簡單應用程序來說,是一個很好的選擇。 性能表現則依賴於使用的數據庫,對某些數據庫來說,使用連接池並不重要,這個配置就很適合這種情形。
POOLED: 這種數據源的實現利用“池”的概念將 JDBC 連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間。 這種處理方式很流行,能使併發 Web 應用快速響應請求。
JNDI – 這個數據源實現是爲了能在如 EJB 或應用服務器這類容器中使用,容器可以集中或在外部配置數據源,然後放置一個 JNDI 上下文的數據源引用。
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
3.屬性(properties)
我們可以通過properties屬性來實現引用配置文件
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。【db.properties】
編寫配置文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username=xxx
password=xxx
mybatis-config.xml文件配置:
<!--引入外部配置文件-->
<properties resource="db.properties"/>
<!-直接配置:可以增加,或者全部配置-->
<!-若外部配置文件與這裏重複,優先外部-->
<!--<properties resource="db.properties">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
常見錯誤:
4.類型別名(typeAliases)
- 類型別名可爲 Java 類型設置一個縮寫名字。
- 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。
1.直接指定一個類,然後給他起別名(可以DIY別名)
Mybatis.xml文件:
<!--可以給實體類起別名-->
<typeAliases>
<typeAlias type="com.lxf.pojo.User" alias="User"/>
</typeAliases>
UserMapper.xml:
<!--select查詢語句,查詢id對應的User-->
<select id="getUserById" resultType="User" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
2.也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean。
掃描實體類的包,它的默認別名就爲這個類的類名,首字母小寫。就例如掃描com.lxf.pojo,pojo下有兩個類:User、Student
那麼他們兩個的包路徑別名分別爲user、student(包名多的時候非常有用,默認是類名首字母小寫).
如要DIY類名:在這個包下的類上加上註解:@Alias(“xxx”)
Mybatis.xml文件:
<!--可以給實體類起別名-->
<typeAliases>
<package name="com.lxf.pojo"/>
</typeAliases>
UserMapper.xml:
<!--select查詢語句,查詢id對應的User-->
<select id="getUserById" resultType="User" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
5.設置
6.映射器
MapperRegistry:註冊綁定我們的Mapper文件;
方式一【推薦使用】:
<!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
<mappers>
<mapper resource="com/lxf/dao/UserMapper.xml"></mapper>
</mappers>
方式二:使用class文件綁定註冊
<!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
<mappers>
<mapper class="com.lxf.dao.UserMapper"/>
</mappers>
注意點:
-
接口和他的Mapper配置文件必須同名!
-
接口和他的Mapper配置文件必須在同一個包下!
方式三:使用掃描包進行注入
<!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
<mappers>
<package name="com.lxf.dao"/>
</mappers>
注意點:
-
接口和他的Mapper配置文件必須同名!
-
接口和他的Mapper配置文件必須在同一個包下!
7.生命週期與作用域
生命週期和作用域,是至關重要的,因爲錯誤的使用會導致非常嚴重的併發問題
SqlSessionFactory
- 一旦創建了SqlSessionFactory,就不再需要它了
- 局部變量
SqlSessionFactory:
- SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例。
- 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複創建多次,多次重建 SqlSessionFactory 被視爲一種代碼“壞習慣”。
- 因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。
- 類似於全局變量
SqlSession
- 每個線程都應該有它自己的 SqlSession 實例(連接到連接池的一個請求!)。
- SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的託管作用域中,比如 Servlet 框架中的 HttpSession。
- 用完即關閉,類似於局部變量
4.解決屬性名與字段名不一致的問題
數據庫的字段:
新建一個項目,拷貝之前的,測試實體類字段不一致的情況
測試問題:查出來的字段password爲null(數據庫不爲null)
select * from mybatis.user where id=#{id};
類型處理器
select id,name,pwd from mybatis.user where id=#{id};
解決方法:
-
起別名
select id,name,pwd as password from mybatis.user where id=#{id};
-
resultMap
結果集映射
resultMap
元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets
數據提取代碼中解放出來,並在一些情形下允許你進行一些 JDBC 不支持的操作。實際上,在爲一些比如連接的複雜語句編寫映射代碼的時候,一份 resultMap
能夠代替實現同等功能的數千行代碼。ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
id name pwd
id name password
UserMapper.xml文件配置:
<!--select查詢語句,查詢id對應的User-->
<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from mybatis.user where id=#{id};
</select>
<!--結果集映射-->
<resultMap id="UserMap" type="user">
<!--column:數據庫中的字段,property:實體類中的屬性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
5.日誌
5.1日誌工廠
-
如果一個數據庫操作,出現了異常,我們需要排錯。
-
日誌可以打印清晰的日誌信息,方便我們查詢錯誤信息、sql信息、步驟信息等
-
曾經:sout、debug。現在:日誌
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在Mybatis具體使用哪一日誌,在設置中設定!
1.STDOUT_LOGGING 標準日誌輸出Mapper.xml配置:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
注意:logImpl大小寫要注意,名字和值都不能多出空格
2.log4j
- Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日誌信息輸送的目的地是控制檯、文件、GUI組件,
- 我們也可以控制每一條日誌的輸出格式
- 通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程。
- 最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。
(1)、先導入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
(2)、log4j.properties
#將等級爲DEBUG的日誌信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger=DEBUG,console,file
#控制檯輸出到相關配置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%N
#文件輸出的相關配置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/lxf.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日誌輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3.配置log4j爲日誌的實現
<!--設置-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.log4j的使用
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=65112:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;C:\Users\dell\IdeaProjects\20200604-Mybatis-study\mybatis-03\target\test-classes;C:\Users\dell\IdeaProjects\20200604-Mybatis-study\mybatis-03\target\classes;E:\maven\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;E:\maven\repository\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;E:\maven\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;E:\maven\repository\junit\junit\4.12\junit-4.12.jar;E:\maven\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.lxf.dao.UserMapperTest,test
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
[org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
[org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
[org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Listing file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao/UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao/UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ���� 4 W
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Listing file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ���� 4 getUserById (I)Lcom/lxf/pojo/User;
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Reader entry: <?xml version="1.0" encoding="UTF-8" ?>
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.lxf.dao.UserMapperTest matches criteria [is assignable to Object]
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.lxf.dao.UserMapper matches criteria [is assignable to Object]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1095293768.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[com.lxf.dao.UserMapper.getUserById]-==> Preparing: select * from mybatis.user where id=?;
[com.lxf.dao.UserMapper.getUserById]-==> Parameters: 1(Integer)
[com.lxf.dao.UserMapper.getUserById]-<== Total: 1
user = User{id=1, name='劉一手', password='123'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1095293768 to pool.
Process finished with exit code 0
4.簡單使用
- 在要使用的類中,導入import org.apache.log4j.Logger;
- new一個logger對象,開始測試
private static Logger logger=Logger.getLogger(UserMapperTest.class);
@Test
public void logTest(){
logger.info("成功進入");
logger.debug("debug信息");
logger.error("錯誤信息");
}
- 結果
[com.lxf.dao.UserMapperTest]-成功進入
[com.lxf.dao.UserMapperTest]-debug信息
[com.lxf.dao.UserMapperTest]-錯誤信息
6.分頁
爲什麼分頁:減少數據的處理量
6.1使用Limit分頁
select * from user limit startIndex,pageSize;
select * from user limit 0,2;
使用Mybatis實現分頁,核心SQL
- 接口
//分頁查詢
List<User> getUserBylimit(Map<String,Integer> map);
- Mapper.xml
<!-- 分頁查詢-->
<select id="getUserBylimit" resultMap="userMap" parameterType="map">
select * from user limit #{startIndex},#{pageSize};
</select>
- 測試
@Test
public void getUserByLimit(){
//獲取SqlSession對象
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String,Integer> map=new HashMap<String,Integer>();
//給map添加鍵值對
map.put("startIndex",1);
map.put("pageSize",2);
//執行方法
List<User> userBylimit = mapper.getUserBylimit(map);
for (User user : userBylimit) {
System.out.println("user = " + user);
}
//關閉session
session.close();
}
6.2RowBounds分頁
UserMapper.java接口:
//分頁查詢2
List<User> getUserByRoundBounds();
UserMapper.xml配置文件:
<!-- 分頁查詢2-->
<select id="getUserByRoundBounds" resultMap="userMap">
select * from user;
</select>
測試類:
@Test
public void getUserByRowBounds(){
//獲取SqlSession對象
SqlSession session = MybatisUtil.getSession();
//RowBounds實現
RowBounds rowBounds = new RowBounds(1, 3);
List<User> users = session.selectList("com.lxf.dao.UserMapper.getUserByRoundBounds", null, rowBounds);
for (User user : users) {
System.out.println("user = " + user);
}
//關閉session
session.close();
}
6.3分頁插件(瞭解即可)
7.使用註解開發
7.1運行過程
- 註解在接口上實現
@Select(value = "select * from user")
List<User> getUsers();
- 需要在覈心配置文件中綁定
<!--綁定接口-->
<mappers>
<mapper class="com.lxf.dao.UserMapper"/>
</mappers>
3.測試(和之前的Mapper方法一樣)
運行過程拓展:
7.2增刪改查
1.UserMapper.java接口類
package com.lxf.dao;
import com.lxf.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
@Select(value = "select * from user")
List<User> getUsers();
//方法存在多個參數,必須每個參數前面必須加上@Param註解
@Select(value = "select * from user where id =#{id}")
User getUserById(int id2);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set id=#{id},name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id2}")
int deleteUser(@Param("id2") int id);
}
2.測試類
import com.lxf.dao.UserMapper;
import com.lxf.pojo.User;
import com.lxf.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
//查詢一個數據
@Test
public void test(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// List<User> users = mapper.getUsers();
//
// for (User user : users) {
// System.out.println("user = " + user);
// }
User user = mapper.getUserById(1);
System.out.println("user = " + user);
session.close();
}
//增加一個數據
@Test
public void test2(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.addUser(new User(15,"成龍","cl"));
if(i>0){
System.out.println("插入成功!");
}
session.close();
}
//更新一個數據
@Test
public void test3(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(14,"劉小法","lxf"));
if(i>0){
System.out.println("更新成功!");
}
session.close();
}
//刪除一個用戶
@Test
public void test4(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.deleteUser(5);
if(i>0){
System.out.println("刪除成功!");
}
session.close();
}
}
3.注意:
- 我已經在工具類設置自動提交,所以增刪改不需要提交事務
//返回selsession,並設置自動提交爲true
return sqlSessionFactory.openSession(true);
- 在使用註解開發時,方法存在多個參數,必須每個參數前面必須加上@Param註解,而且如果Param指定名字,sql語句中的也要對應
@Delete("delete from user where id=#{id2}")
int deleteUser(@Param("id2") int id);
- 接口類也要註冊到mybatis-config.xml文件中
<!--綁定接口-->
<mappers>
<mapper class="com.lxf.dao.UserMapper"/>
</mappers>
8.多對一處理
- 對學生而言,關聯,多個學生關聯一個老師【多對一】
- 對老師而言,集合,一個老師教多個學生【一對多】
SQl數據庫建立對應的表:
CREATE TABLE `teacher`(
`id` INT(10) not NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(id,name) VALUES(1,'劉老師');
create table `student`(
`id` INT(10) not null PRIMARY KEY,
`name` VARCHAR(30) DEFAULT null,
`tid` INT(10) DEFAULT null,
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO student(id,name,tid) VALUES(1,'小明',1);
INSERT INTO student(id,name,tid) VALUES(2,'小紅',1);
INSERT INTO student(id,name,tid) VALUES(3,'小張',1);
INSERT INTO student(id,name,tid) VALUES(4,'小李',1);
INSERT INTO student(id,name,tid) VALUES(5,'小王',1);
測試環境搭建
- 導入lombok
- 新建實體類Teacher、Student
- 建立Mapper接口
- 建立Mapper.xml文件
- 在覈心配置文件中綁定註冊我們的Mapper接口或者文件!
- 測試查詢是否成功!
8.1按照查詢嵌套處理
Student類:
package com.lxf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//學生需要關聯一個老師!
private Teacher teacher;
}
Teacher類
package com.lxf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
StudentMapper.java接口
package com.lxf.dao;
import com.lxf.pojo.Student;
import java.util.List;
public interface StudentMapper {
//查詢所有的學生信息,以及對應的老師的信息!
List<Student> getStudents();
}
StudentMapper.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.lxf.dao.StudentMapper">
<select id="getStudents" resultMap="studentMap" >
select * from student
</select>
<resultMap id="studentMap" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.lxf.pojo.Teacher">
select * from teacher where id=#{id};
</select>
</mapper>
測試類:
@Test
public void getStudents(){
SqlSession sqlSession = MybatisUtil.getSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = studentMapper.getStudents();
for (Student student: students) {
System.out.println("student = " + student);
}
sqlSession.close();
}
8.2按照結果嵌套處理
StudentMapper.java接口類:
//查詢所有的學生信息,以及對應的老師的信息!
List<Student> getStudents2();
StudentMapper.xml配置文件:
<!--方式二:按結果嵌套處理-->
<select id="getStudents2" resultMap="studentMap2">
select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id;
</select>
<resultMap id="studentMap2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher" >
<result property="id" column="sid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
測試類:
@Test
public void getStudents2(){
SqlSession sqlSession = MybatisUtil.getSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = studentMapper.getStudents2();
for (Student student: students) {
System.out.println("student = " + student);
}
sqlSession.close();
}
9.一對多處理
比如:一個老師擁有多個學生!對於老師而言就是一對多的關係
1.環境搭建,和第8點一樣
Student實體類:
package com.lxf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//學生需要關聯一個老師!
private int tid;
}
Teacher實體類:
package com.lxf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
//一個學生擁有多個學生
private List<Student> students;
}
9.1按照查詢嵌套查詢
TeacherMapper.java接口類:
//獲取這個老師及其所有的學生
Teacher getTeachers2(@Param("tid") int id);
TeacherMapper.java配置文件:
<!--方式一:按查詢嵌套處理-->
<select id="getTeachers2" resultMap="teacherMap2" >
select * from mybatis.teacher where id=#{tid};
</select>
<resultMap id="teacherMap2" type="com.lxf.pojo.Teacher">
<result column="id" property="id"/>
<result column="name" property="name"/>
<collection property="students" column="id" javaType="ArrayList" ofType="com.lxf.pojo.Student" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="com.lxf.pojo.Student">
select * from mybatis.student where tid=#{tid};
</select>
測試類:
@Test
public void getTeachers2(){
SqlSession sqlSession = MybatisUtil.getSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeachers2(1);
System.out.println("teacher = " + teacher);
sqlSession.close();
}
9.2按照結果嵌套查詢
TeacherMapper.java接口類:
//獲取這個老師及其所有的學生
Teacher getTeachers3(@Param("tid") int id);
TeacherMapper.java配置文件:
<!--方式二:按結果嵌套處理-->
<select id="getTeachers3" resultMap="teacherMap3">
select t.id tid,t.name tname,s.id sid,s.name sname,s.tid stid from teacher t,student s where t.id=#{tid} and s.tid=#{tid};
</select>
<resultMap id="teacherMap3" type="com.lxf.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="com.lxf.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
</collection>
</resultMap>
測試類:
@Test
public void getTeachers3(){
SqlSession sqlSession = MybatisUtil.getSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeachers3(1);
System.out.println("teacher = " + teacher);
sqlSession.close();
}
小結:
-
關聯-association【多對一】
-
集合-collection【一對多】
-
javaType & ofType
- javaType 用來指定實體類中屬性的類型
- ofType用來指定映射到List或者集合中的pojo類型,泛型中的約束類型
注意點:
- 保證sql的可讀性
- 注意一對多和多對一中,屬性名和字段的問題!
- 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j
10.動態SQL
什麼是動態sql:動態SQL就是根據不同的條件生成不同的SQL語句
如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
10.1搭建環境
sql語句:
create table `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客標題',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` VARCHAR(30) NOT NULL COMMENT '創建時間',
`views` INT(30) not null COMMENT '瀏覽量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
創建一個基礎工程
- 導包
- 編寫配置文件
- 編寫實體類
- 編寫實體類對應的Mapper接口和Mapper.XML文件
10.2增加數據
BlogMapper.java接口文件:
//插入數據
int addBlog(Blog blog);
BlogMapper.xml配置文件:
<insert id="addBlog" parameterType="com.lxf.pojo.Blog">
insert into mybatis.blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
測試類
@Test
public void addBlog(){
SqlSession sqlSession = MybatisUtil.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
int i = mapper.addBlog(new Blog(IDUtils.getID(), "Mybatis學習方法", "劉一手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 1060));
int j = mapper.addBlog(new Blog(IDUtils.getID(), "SpringMVC學習方法", "劉二手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 530));
int k = mapper.addBlog(new Blog(IDUtils.getID(), "Spring5學習方法", "劉三手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 777));
int l = mapper.addBlog(new Blog(IDUtils.getID(), "SSM學習方法", "劉四手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 666));
if(i>0&&j>0&&k>0&&l>0){
System.out.println("成功插入四條數據!");
}
sqlSession.close();
}
10.3 IF查詢
BlogMapper.java接口類
//查詢博客
List<Blog> queryBlogIF(Map map);
BlogMapper.xml配置文件:
<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog where 1=1
<if test="title !=null">
and title=#{title}
</if>
<if test="author !=null">
or author=#{author}
</if>
</select>
測試類:
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtil.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("title","Mybatis學習方法");
map.put("author","劉四手");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println("blog = " + blog);
}
sqlSession.close();
}
10.4choose(when、otherwise)查詢
有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
還是上面的例子,但是策略變爲:傳入了 “title” 就按 “title” 查找,傳入了 “author” 就按 “author” 查找的情形。若兩者都沒有傳入,就返回views大於多少的 BLOG(這可能是管理員認爲,與其返回大量的無意義隨機 Blog,還不如返回一些由管理員挑選的 Blog)。
BlogMapper.java接口類:
//查詢博客
List<Blog> queryBlogChoose(Map map);
BlogMapper.xml配置文件:
<select id="queryBlogChoose" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog
<!--where在10.5中有示例-->
<where>
<choose>
<when test="title !=null">
title=#{title}
</when>
<when test="author !=null">
author=#{author}
</when>
<otherwise>
views > #{views}
</otherwise>
</choose>
</where>
</select>
測試類:
@Test
public void queryBlogChoose(){
SqlSession sqlSession = MybatisUtil.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("title","Mybatis學習方法");
map.put("author","劉四手");
map.put("views",666);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println("blog = " + blog);
}
sqlSession.close();
}
10.5trim、where、set標籤
1.where標籤
思考:如果沒有where 1=1這個條件該怎麼查?以下有解決方法:
<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog where
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
or author=#{author}
</if>
</select>
如果沒有匹配的條件會怎麼樣?最終這條 SQL 會變成這樣:
SELECT * FROM BLOG
WHERE
這會導致查詢失敗。如果匹配的只是第二個條件又會怎樣?這條 SQL 會是這樣:
SELECT * FROM BLOG
WHERE
or author=#{author}
這個查詢也會失敗。這個問題不能簡單地用條件元素來解決。這個問題是如此的難以解決,以至於解決過的人不會再想碰到這種問題。
MyBatis 有一個簡單且適合大多數場景的解決辦法。而在其他場景中,可以對其進行自定義以符合需求。而這,只需要一處簡單的改動:
<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog
<where>
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
or author=#{author}
</if>
</where>
</select>
where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭爲 “AND” 或 “OR”,where 元素也會將它們去除。
2.set標籤
用於動態更新語句的類似解決方案叫做 set。set 元素可以用於動態包含需要更新的列,忽略其它不更新的列,它會檢測,是否多餘,多餘則會自動去掉,比如下面最後views參數沒傳遞就會造成,多餘。
BlogMapper.java接口類:
//更新數據
int updateBlogSet(Map map);
BlogMapper.xml配置文件:
<!--update set標籤-->
<update id="updateBlogSet" parameterType="map" >
update blog
<set>
<if test="title !=null">
title=#{title},
</if>
<if test="author !=null">
author=#{author},
</if>
<if test="views !=null">
views=#{views}
</if>
</set>
where id=#{id}
</update>
測試類:
@Test
public void updateBlogSet(){
SqlSession sqlSession = MybatisUtil.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("title","Mybatis學習簡便方法");
map.put("author","劉五手");
//map.put("views",690);
map.put("id","72bae714f52f4cf196e194114cb35b1f");
int i = mapper.updateBlogSet(map);
if(i>0){
System.out.println("更新數據成功");
}
sqlSession.close();
}
3.trim
where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭爲 “AND” 或 “OR”,where 元素也會將它們去除。
如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定製 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素爲:
<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
or author=#{author}
</if>
</trim>
</select>
prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。
set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。
來看看與 set 元素等價的自定義 trim 元素吧:
<!--update set標籤-->
<update id="updateBlogSet" parameterType="map" >
update blog
<trim prefix="SET" suffixOverrides=",">
<if test="title !=null">
title=#{title},
</if>
<if test="author !=null">
author=#{author},
</if>
<if test="views !=null">
views=#{views}
</if>
</trim>
where id=#{id}
</update>
注意,我們覆蓋了後綴值設置,並且自定義了前綴值。一般上面兩種用where和set就可以了,如果mybatis沒有的建議再用trim
10.6foreach
舉例(查詢一些指定id的Blog):
BlogMapper.java接口類:
//查詢第多個記錄的博客
List<Blog> queryBlogForeach(Map map);
BlogMapper.xml配置文件:
<!--foreach標籤-->
<select id="queryBlogForeach" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from blog
<where>
/*
collection:要循環的list名字
open:開始語句
separator:每個循環後添加的分隔符(除最後一次)
close:結束語句
item:list中每個數據
index:list的下標
*/
<foreach collection="ids" separator="," open="id in (" close=")" item="item">
#{item}
</foreach>
</where>
</select>
測試類:
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtil.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
List list=new ArrayList();
list.add("4");
list.add("3");
list.add("1");
map.put("ids",list);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println("blog = " + blog);
}
sqlSession.close();
}
10.7 SQL片段
- 目的:實現SQL代碼的複用:
<!--SQl片段-->
<sql id="if-title-authorOR">
<if test="title !=null">
title=#{title}
</if>
<if test="author !=null">
or author=#{author}
</if>
</sql>
<!--where if查詢-->
<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
select * from mybatis.blog
<where>
<include refid="if-title-authorOR"></include>
</where>
</select>
10.8bind
bind
元素允許你在 OGNL 表達式以外創建一個變量,並將其綁定到當前的上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
10.9多數據庫支持
如果配置了 databaseIdProvider,你就可以在動態代碼中使用名爲 “_databaseId” 的變量來爲不同的數據庫構建特定的語句。比如下面的例子:
<insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>
11.緩存
11.1、簡介
查詢:連接數據庫,耗資源!
一次查詢的結果,給他暫存到一個可以直接取得地方!–>內存;緩存
我們再次查詢相同得數據時,直接走緩存,就不用走數據庫了
- 什麼是緩存[Cache]?
-
存在內存中的臨時數據。
-
將用戶經常查詢到的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關係型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢的效率,解決了高併發系統的性能問題
- 爲什麼使用緩存
- 減少和數據庫的交互次數,減少系統開銷,提高系統效率。
- 什麼樣的數據能使用緩存?
- 經常查詢並且不經常改變的數據
11.2、Mybatis緩存
- MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定製和配置緩存。緩存可以極大的提升查詢效率。
- MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存
- 默認情況下,只有一級緩存開啓。(SqlSession級別的緩存,也稱爲本地緩存)
- 二級緩存需要手動開啓和配置,它是基於namespace級別的緩存。
- 爲了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存
13.3、一級緩存
-
一級緩存也叫本地緩存
- 與數據庫同一次會話期間查詢到的數據會放在本地緩存中。
- 以後如果需要獲取相同的數據,直接從緩存中拿,沒必要再去查詢數據庫;
-
緩存失效的情況:
- 查詢不同的東西
- 查詢之間有增刪改操作,可能會改變原來的數據,所以必定會刷新緩存!
- 手動清除緩存也會導致緩存失效
14.4、二級緩存
- 二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存
- 基於namespace級別的緩存,一個命名空間對應一個二級緩存
- 工作機制
- 一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;
- 如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;
- 新的會話查詢信息,就可以從二級緩存中獲取內容;
- 不同的mapper查出的數據會放在自己對應的緩存(map)中
步驟:
- 開啓全局緩存
<!--開啓全局緩存-->
<setting name="cacheEnabled" value="true"/>
- UserMapper文件配置
<cache eviction="FIFO" flushInterval="600000" size="500" />
UserMapper.java接口類:
//查詢所有的User
List<User> getUsers();
UserMapper.xml配置文件
<select id="getUsers" resultType="com.lxf.pojo.User">
select * from user;
</select>
<cache eviction="FIFO" flushInterval="600000" size="500" />
測試類:
@Test
public void getUsers(){
SqlSession sqlSession1 = MybatisUtil.getSession();
SqlSession sqlSession2 = MybatisUtil.getSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> users1 = mapper1.getUsers();
for (User user : users1) {
System.out.println("user = " + user);
}
sqlSession1.close();
System.out.println("==========================================");
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
List<User> users2 = mapper2.getUsers();
for (User user : users2) {
System.out.println("user = " + user);
}
sqlSession2.close();
}
問題:未將實體類序列化而報錯。解決方法:實體類繼承serializable接口就可以了
14.5、小結:
- 只有開啓了二級緩存,在一個Mapper下有效
- 所有的數據都會先放在一級緩存中;
- 只有當會話提交,或者關閉的時候,纔會提交到二級緩存中
14.6、自定義緩存Ehcache
Ehache是一種廣泛使用的開源Java分佈緩存,主要面向通用緩存
-
導依賴
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </depenency>
-
UserMapper.xml配置:
<!--eh緩存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
- resource目錄下建一個ehcache.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
name:緩存名稱
maxElementsInMemory:緩存中最大允許創建的對象數
maxInMemory:設定內存中創建對象的最大值。
eternal:設置元素(譯註:內存中對象)是否永久駐留。如果是,將忽略超時限制且元素永不消亡。
overflowToDisk:設置當內存中緩存達到 maxInMemory 限制時元素是否可寫到磁盤上。
timeToIdleSeconds:設置某個元素消亡前的停頓時間。
timeToLiveSeconds:爲元素設置消亡前的生存時間.
memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置爲FIFO(先進先出)或是LFU(較少使用)。
diskPersistent:重啓時內存不持久化到硬盤。
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>