MyBatis
MyBatis簡介
MyBatis作用
- MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。
- MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。
- MyBatis可以使用簡單的XML用於配置和原始映射,將接口和Java的POJO類映射成數據庫中的記錄
- 使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
歷史
- 原是apache的一個開源項目iBatis
- 2010年6月這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis 。
- iBATIS一詞來源於“internet”和“abatis”的組合,是一個基於Java的持久層框架。
為什么要使用MyBatis?
JDBC
- SQL夾在Java代碼塊里,耦合度高導致硬編碼內傷
- 維護不易且實際開發需求中sql是有變化,頻繁修改的情況多見
- 要自已創建connection、創建statement、手動設置參數、結果集檢索等
Hibernate
- 長難復雜SQL,對於Hibernate而言處理也不容易
- 內部自動生產的SQL,不容易做特殊優化。
- 基於全映射的全自動框架,javaBean存在大量字段時無法只映射部分字段。導致數據庫性能下降。
Mybatis
- 對開發人員而言,核心sql還是需要自己優化
- MyBatis是一個半自動化的持久化層框架。
- MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。
MyBatis入門程序
1.下載Mybatis核心包
http://www.mybatis.org/mybatis-3/getting-started.html
https://github.com/mybatis/mybatis-3/releases
2.創建工程,引入MyBatis核心包及依賴包
ant-1.9.6.jar
ant-launcher-1.9.6.jar
asm-5.2.jar
cglib-3.2.5.jar
commons-logging-1.2.jar
javassist-3.22.0-GA.jar
junit-4.9.jar
log4j-1.2.17.jar
log4j-api-2.3.jar
log4j-core-2.3.jar
lombok.jar
mybatis-3.4.6.jar
mysql-connector-java-5.1.7-bin.jar
ognl-3.1.16.jar
slf4j-api-1.7.25.jar
slf4j-log4j12-1.7.25.jar
3.創建customer表,建立與表對象的domain
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter@Getter@ToString
public class Customer {
private Integer cust_id;
private String cust_name;
private String cust_profession;
private String cust_phone;
private String email;
}
4.創建MyBatis核心配置文件SqlMappingConfig.xml
<?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>
<!-- spring整合后 environments配置將廢除 使用spring中的連接池 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="1234" />
</dataSource>
</environment>
</environments>
</configuration>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- spring整合后 environments配置將廢除 使用spring中的連接池 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="1234" />
</dataSource>
</environment>
</environments>
</configuration>
5.創建與表對象的關系映射Mapping文件編寫sql語句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="myTest">
<!--根據cust_id查詢客戶-->
<select id="queryCustomerById" parameterType="Int" resultType="com.le.domain.Customer">
SELECT * FROM `customer` WHERE cust_id = #{cust_id}
</select>
</mapper>
6.在SqlMappingConfig.xml核心配置文件當中引入Mapping
<!--加載映射文件-->
<mappers>
<mapper resource="com/le/domain/Customer.xml"></mapper>
</mappers>
7.創建工廠,執行sql語句
@Test
public void test() throws IOException {
/* //1.sqlSessionFactoryBuilder 加載配置文件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.讀取配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMappingConfig.xml");
//3.獲取session工廠
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
//4.獲取會話 ---JDBC 連接
SqlSession sqlSession = sessionFactory.openSession();*/
SqlSession sqlSession = MybatisUtils.openSession();
//5.執行sql
Customer customer = sqlSession.selectOne("queryCustomerById", 2);
System.out.println(customer);
//6.關閉session
sqlSession.close();
}
MyBatis核心Api
SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder用於創建SqlSessionFacoty
- SqlSessionFacoty一旦創建完成就不需要SqlSessionFactoryBuilder了
- 因為SqlSession是通過SqlSessionFactory創建的
- 所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用范圍是方法范圍即方法體內局部變量。
SqlSessionFactory
- 創建sqlSession的工廠,是一個接口
- 接口中定義了openSession的不同重載方法
- SqlSessionFactory的最佳使用范圍是整個應用運行期間,一旦創建后可以重復使用,通常以單例模式管理SqlSessionFactory。
SqlSession
- 連接到數據庫的一個會話
- sqlSession中定義了數據庫操作方法。
- 每個線程都應該有它自己的SqlSession實例
- SqlSession的實例不能共享使用,它也是線程不安全的。因此最佳的范圍是請求或方法范圍
- 絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
MyBatis架構
MyBatis-查詢
MybatisUtils工具類
package com.le.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.IOException;
import java.io.InputStream;
public class MybatisUtils {
public static final SqlSessionFactory sessionFactory;
static {
//1.sqlSessionFactoryBuilder 加載配置文件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.讀取配置文件
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("SqlMappingConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
//3.獲取session工廠
sessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
}
public static SqlSession openSession(){
return sessionFactory.openSession();
}
}
查詢所有客戶
<!--查詢所有-->
<select id="queryAllCustomer" resultType="com.le.domain.Customer">
SELECT * FROM `customer`
</select>
@Test
public void test2() {
SqlSession sqlSession = MybatisUtils.openSession();
/*查詢所有的用戶*/
List<Customer> queryAllCustomer = sqlSession.selectList("queryAllCustomer");
for (Customer customer : queryAllCustomer) {
System.out.println(customer);
}
sqlSession.close();
}
根據用戶名模糊查詢客戶
方式1
<!--根據用戶名模糊查詢客戶-->
<select id="querytCustomerByName" parameterType="String" resultType="com.le.domain.Customer">
select * from customer where cust_name like '%{value}%';
</select>
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.openSession();
List<Customer> customers = sqlSession.selectList("querytCustomerByName", "%李%");
for (Customer customer : customers) {
System.out.println(customer);
}
sqlSession.close();
}
方式2
<!--根據用戶名模糊查詢客戶-->
<select id="querytCustomerByName" parameterType="String" resultType="com.le.domain.Customer">
select * from customer where cust_name like #{name};
</select>
總結
parameterType
指定輸入參數類型
mybatis通過ognl從輸入對象中獲取參數值拼接在sql中
resultType
指定輸出結果類型
mybatis將sql查詢結果的一行記錄數據映射為resultType指定類型的對象。如果有多條數據,則分別進行映射,並把對象放到容器List中
selectOne
查詢一條記錄
如果使用selectOne查詢多條記錄則拋出異常
selectList
可以查詢一條或多條記錄
#{}和${}
#{}
表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值
自動進行java類型和jdbc類型轉換
#{}可以有效防止sql注入
#{}可以接收簡單類型值或pojo屬性值
如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱
${}
表示拼接sql串
通過${}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換
${}可以接收簡單類型值或pojo屬性值
如果parameterType傳輸單個簡單類型值,${}括號中只能是value
保存更新刪除
添加客戶,返回添加過后自增的主鍵
<!--添加-->
<insert id="insertCustom" parameterType="com.le.domain.Customer">
/*獲取插入的最后一個id*/
<selectKey keyColumn="cust_id" keyProperty="cust_id" resultType="Integer" order="AFTER">
select last_insert_id()
</selectKey>
insert into `customer`(cust_name,cust_profession,cust_phone,email)
values (#{cust_name},#{cust_profession},#{cust_phone},#{email})
</insert>
/*添加客戶*/
@Test
public void insert(){
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = new Customer();
customer.setCust_name("后裔2");
customer.setCust_phone("18907897879");
sqlSession.insert("insertCustom",customer);
//當要改動數據庫當中的記錄時,執行sql時要自己提交事務
//手動提交事務
sqlSession.commit();
System.out.println(customer.getCust_id());
sqlSession.close();
}
更新客戶
<!--更新-->
<update id="updateCustomer" parameterType="com.le.domain.Customer">
update `customer` set cust_name=#{cust_name} where cust_id=#{cust_id}
</update>
/*更新操作*/
@Test
public void update(){
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = sqlSession.selectOne("queryCustomerById", 12);
customer.setCust_name("孫悟空");
sqlSession.update("updateCustomer",customer);
sqlSession.commit();
sqlSession.close();
}
刪除客戶
<!--刪除操作-->
<delete id="deleteCustomer" parameterType="com.le.domain.Customer">
delete from `customer` where cust_id=#{cust_id}
</delete>
/*刪除*/
@Test
public void delete(){
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = sqlSession.selectOne("queryCustomerById", 12);
sqlSession.delete("deleteCustomer",customer);
sqlSession.commit();
sqlSession.close();
}
MyBatis開發DAO
原始Dao開發方法
package com.le.dao;
import com.le.domain.Customer;
import java.util.List;
public interface CustomerDao {
public Customer getCustomerWithId(Integer id);
public List<Customer> getAllCustomer();
public void addCustomer(Customer customer);
public void updateCustomer(Customer customer);
}
package com.le.dao;
import com.le.domain.Customer;
import com.le.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class CustomerDaoImpl implements CustomerDao {
@Override
public Customer getCustomerWithId(Integer id) {
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = sqlSession.selectOne("queryCustomerById", id);
return customer;
}
@Override
public List<Customer> getAllCustomer() {
SqlSession sqlSession = MybatisUtils.openSession();
List<Customer> customers = sqlSession.selectList("queryAllCustomer");
return customers;
}
@Override
public void addCustomer(Customer customer) {
SqlSession sqlSession = MybatisUtils.openSession();
sqlSession.insert("insertCustom",customer);
}
@Override
public void updateCustomer(Customer customer) {
SqlSession sqlSession = MybatisUtils.openSession();
sqlSession.update("insertCustom",customer);
}
}
@Test
public void test() throws IOException {
//1.sqlSessionFactoryBuilder 加載配置文件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.讀取配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMappingConfig.xml");
//3.獲取session工廠
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
//4.獲取會話 ---JDBC 連接
SqlSession sqlSession = sessionFactory.openSession();
//5.執行sql
Customer customer = sqlSession.selectOne("queryCustomerById", 2);
System.out.println(customer);
//6.關閉session
sqlSession.close();
}
Mapper動態代理
要求
- namespace必須和Mapper接口類路徑一致
- id必須和Mapper接口方法名一致
- parameterType必須和接口方法參數類型一致
- resultType必須和接口方法返回值類型一致
過程
package com.le.mapper;
import com.le.domain.Customer;
import java.util.List;
public interface CustomerMapper {
//根據cust_id查詢客戶
public Customer queryCustomerById(Integer id);
}
<?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.le.mapper.CustomerMapper">
<!--根據cust_id查詢客戶-->
<select id="queryCustomerById" parameterType="Integer"
resultType="com.le.domain.Customer">
SELECT * FROM `customer` WHERE cust_id = #{cust_id}
</select>
</mapper>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.queryCustomerById(1);
System.out.println(customer);
sqlSession.close();
}
selectOne和selectList
- 動態代理對象調用sqlSession.selectOne()和sqlSession.selectList()是根據mapper接口方法的返回值決定
- 如果返回list則調用selectList方法,如果返回單個對象則調用selectOne方法。
參數傳遞
單個參數
- 可以接受基本類型,對象類型,集合類型的值。
- MyBatis可直接使用這個參數,不需要經過任何處理。
多個參數
- 任意多個參數,都會被MyBatis重新包裝成一個Map傳入。
- Map的key是param1,param2…,值就是參數的值。
示例
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.queryCustomerById(1,"魯班大師");
System.out.println(customer);
sqlSession.close();
}
<!--根據cust_id 和 cust_name查詢客戶-->
<select id="queryCustomerById"
resultType="com.le.domain.Customer">
SELECT * FROM `customer` WHERE cust_id = #{arg0} and cust_name=#{arg1}
</select>
@param命名參數
- 為參數使用@Param起一個名字,
- MyBatis就會將這些參數封裝進map中,key就是我們自己指定的名字
示例
public interface CustomerMapper {
/*
* 根據cust_id查詢客戶
* */
public Customer queryCustomerById(@Param("cust_id") Integer id,@Param("cust_name") String cust_name);
}
<!--根據cust_id 和 cust_name查詢客戶-->
<select id="queryCustomerById"
resultType="com.le.domain.Customer">
SELECT * FROM `customer` WHERE cust_id = #{arg0} and cust_name=#{arg1}
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.queryCustomerById(1,"魯班大師");
System.out.println(customer);
sqlSession.close();
}
POJO
- 當這些參數屬於我們業務POJO時,我們直接傳遞POJO
示例
public void insertCustom(Customer customer);
@Test
public void insert(){
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = new Customer();
customer.setCust_name("后裔2");
customer.setCust_phone("18907897879");
sqlSession.insert("insertCustom",customer);
//當要改動數據庫當中的記錄時,執行sql時要自己提交事務
//手動提交事務
sqlSession.commit();
System.out.println(customer);
sqlSession.close();
}
<!--添加-->
<insert id="insertCustom" parameterType="com.le.domain.Customer">
insert into `customer`(cust_name,cust_profession,cust_phone,email)
values (#{cust_name},#{cust_profession},#{cust_phone},#{email})
</insert>
Map
- 我們也可以封裝多個參數為map,直接傳遞
示例
public Customer queryCustomerById(Map<String,Object> map);
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("cust_id",1);
hashMap.put("cust_name","魯班");
Customer customer = mapper.getCustomerWithID(hashMap);*/
System.out.println(customer);
sqlSession.close();
}
<!--根據cust_id 和 cust_name查詢客戶-->
<select id="queryCustomerById"
resultType="com.le.domain.Customer">
SELECT * FROM `customer` WHERE cust_id = #{cust_id} and cust_name=#{cust_name}
</select>
參數傳遞源碼分析
-
會把參數給放到一個數組當中
-
如果一個參數, 內部處理時,會自動把該參數范圍
-
如果是多個參數,內部會做判斷
-
判斷是否有@param注解
如果沒有@param注解
沒有注解的話, 就直接使用arg0 arg1...為key 放到map中
並且還會以param1和param2...為key放一份到map中
如果有@param注解
如果有注解的話, 會使用注解當中的值,替換掉默認的arg0和arg1
使用@param中的值,做為key 放到一個map當中
並且還會以param1和param2...為key放一份到map中
MaBatis核心配置文件
properties標簽
- 定義屬性及讀取屬性文件
示例
<!-- 是用resource屬性加載外部配置文件 -->
<properties resource="db.properties">
<!-- 在properties內部用property定義屬性 -->
<!-- 如果外部配置文件有該屬性,則內部定義屬性被外部屬性覆蓋 -->
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="1234" />
</properties>
settings標簽
- 這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為
示例
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter@Getter@ToString
public class Customer {
private Integer cust_id;
private String cust_name;
private String cust_profession;
private String cust_phone;
private String email;
}
<!-- 用來配置MyBatis中一些設置 -->
<!-- 開啟駝峰映射,為自定義的SQL語句服務 -->
<!-- 設置其用數據庫字段下划線映射到jaba對象的駝峰式命名屬性,默認為false -->
<settings>
<!-- 打印查詢語句 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--開啟駝峰命名法-->
<setting name="mapUnderscoreToCamelCase" value="true">
</settings>
typeAliases標簽
- 類型別名是為 Java 類型設置一個短的名字
- 定義單個別名
<!--定義別名-->
<typeAliases>
<!--單個別名定義-->
<typeAlias alias="Customer" type="com.le.domain.Customer"/>
</typeAliases>
<!--查詢用戶 ID-->
<select id="getCustomerWithID" resultType="Customer" >
select * from `customers` where cust_id = #{id} and cust_name=#{name}
</select>
-
批量別名定義
如果當前包類與子包類重名,會有異常
可以在類上使用注解@Alias("別名")
<!--定義別名-->
<typeAliases>
<!--批量定義別名, 別名為類名(大小寫不敏感)-->
<package name="com.le.domain"/>
</typeAliases>
typeHandlers標簽
- 無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,
- 還是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
- JDK1.8之后實現全部的JSR310規范
- 日期時間處理上,我們可以使用MyBatis基於JSR310(Date and Time API)
- 編寫的各種日期時間類型處理器。
- MyBatis3.4以前的版本需要我們手動注冊這些處理器,以后的版本都是自動注冊的
Plugins標簽
- 插件是MyBatis提供的一個非常強大的機制,
- MyBatis 允許你在已映射語句執行過程中的某一點進行攔截調用。
- 通過插件來修改MyBatis的一些核心行為。
Environments標簽
- MyBatis可以配置多種環境,比如開發、測試和生產環境需要有不同的配置。
- 每種環境使用一個environment標簽進行配置並指定唯一標識符
- 可以通過environments標簽中的default屬性指定一個環境的標識符來快速的切換環境
- Environment子標簽
transactionManager事務管理
Type有以下取值
JDBC
使用JDBC 的提交和回滾設置,依賴於從數據源得到的連接來管理事務范圍
MANAGED
不提交或回滾一個連接、讓容器來管理事務的整個生命周期
ManagedTransactionFactory
自定義
實現TransactionFactory接口
type=全類名/別名
dataSource數據源
type有以下取值
UNPOOLED
不使用連接池UnpooledDataSourceFactory
POOLED
使用連接池PooledDataSourceFactory
JNDI
在EJB 或應用服務器這類容器中查找指定的數據源
自定義
實現DataSourceFactory接口,定義數據源的獲取方式
實際開發
實際開發中我們使用Spring管理數據源
並進行事務控制的配置來覆蓋上述配置
<!-- spring整合后 environments配置將廢除 使用spring中的連接池 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池 -->
<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>
<environment id="test">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池 -->
<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>
databaseIDProvider標簽
MyBatis 可以根據不同的數據庫廠商執行不同的語句。
可以通過databaseIDProvider標簽來進行設置
<databaseIdProvider type="DB_VENDOR">
<property name="MYSQL" value="mysql"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
示例
<!--查詢用戶 ID-->
<select id="getAllCustomer" resultType="Customer" databaseId="mysql">
select * from `customers`
</select>
mappers標簽
resource屬性
使用相對於類路徑的資源
<!--加載映射文件-->
<mappers>
<mapper resource="com/le/mapping/CustomerMapping.xml"></mapper>
</mappers>
class屬性
- 使用mapper接口類路徑
- 此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中
<!--加載映射文件-->
<mappers>
<mapper class="com.le.mapping.CustomerMapping"></mapper>
</mappers>
package子標簽
- 指定包下的所有mapper接口
- 此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中
<!--加載映射文件-->
<mappers>
<package name="com.le.mapping"/>
</mappers>
輸出類型
輸出簡單類型
<!--查詢總數-->
<select id="getAccountCustomer" resultType="Integer">
select count(*) from customer;
</select>
Map
第1種形式
-
key:是列名
-
value:是列名對應的值
示例
public Map<String,Object> getCustomerWithId(Integer id);
<select id="getCustomerWithId" resultType="java.util.Map">
select * from customer where cust_id=#{id}
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Map<String, Object> customer = customerMapper.getCustomerWithId(2);
System.out.println(customer);
sqlSession.close();
}
第2種形式
- Map<key,自定義對象>
- key為自己指定的數據庫中的列值
示例
@MapKey("cust_name")
public Map<Integer,Customer> getAllCustomer();
<select id="getAllCustomer" resultType="com.le.domain.Customer">
select * from customer;
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Map<Integer, Customer> allCustomer = customerMapper.getAllCustomer();
for(Integer integer:allCustomer.keySet())
{
System.out.println("key="+integer+" value="+allCustomer.get(integer));
}
sqlSession.close();
}
resultMap
- 只有在寫輸出時使用的都是resultType
- 但是resultType要求必須得要字段名稱和數據庫當中的名稱一致時才能有值,否則為null
- 如果sql查詢字段名和pojo的屬性名不一致,可以通過resultMap將字段名和屬性名作一個對應關系
- 表名與domain
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter@Getter@ToString
public class Customer {
private Integer cust_ids;
private String cust_names;
private String cust_professions;
private String cust_phones;
private String email;
}
<resultMap id="customerMap" type="Customer">
<id column="cust_id" property="cust_ids"/>
<result column="cust_name" property="cust_names"/>
<result column="cust_profession" property="cust_professions"/>
<result column="cust_phone" property="cust_phones"/>
<result column="email" property="email"/>
</resultMap>
<select id="getCustomer" resultMap="customerMap">
select * from customer where cust_id=#{id}
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = customerMapper.getCustomer(2);
System.out.println(customer);
sqlSession.close();
}
多表操作
ManyToOne
關系表
查詢
分步查詢
第一步 先查出所有的訂單
<resultMap id="myOrder" type="Order">
<id property="order_id" column="order_id"/>
<result property="order_name" column="order_name"/>
<result property="order_num" column="order_name"/>
<!--分步查詢-->
<association property="customer" javaType="Customer"
select="com.le.mapper.CustomerMapper.getCustomerWithId"
column="cust_id">
</association>
</resultMap>
<select id="getOrders" resultMap="myOrder">
select * from `order`
</select>
第二步 根據id查出對應客戶
<mapper namespace="com.le.mapper.CustomerMapper">
<!--根據id獲取客戶-->
<select id="getCustomerWithId" resultType="com.le.domain.Customer">
SELECT * from customer WHERE cust_id = #{id}
</select>
</mapper>
左連接查詢
查詢所有的訂單及訂單所對應的客戶
- 左連接
- 把左邊表的數據全部查出,右邊表只查出滿足條件的記錄
應對sql
SELECT * FROM `order` as o LEFT JOIN customer as c on o.cus_id = c.cust_id;
建立domain
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter@Getter@ToString
public class Order {
private Integer order_id;
private String order_name;
private String order_num;
private Customer customer;
}
建立Mapping映射
<resultMap id="orderMap" type="Order">
<id property="order_id" column="order_id"/>
<result property="order_name" column="order_name"/>
<result property="order_num" column="order_name"/>
<!--關聯對象賦值-->
<association property="customer" javaType="Customer">
<id property="cust_id" column="cust_id"/>
<result property="cust_name" column="cust_name"/>
<result property="cust_profession" column="cust_profession"/>
<result property="cust_phone" column="cust_phone"/>
<result property="email" column="email"/>
</association>
</resultMap>
<!--查詢所有訂單-->
<select id="getAllOrders" resultMap="orderMap">
SELECT * from `order` as o LEFT JOIN `customer` c on o.cust_id = c.cust_id;
</select>
測試類
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> allOrders = orderMapper.getAllOrders();
for (Order order : allOrders) {
System.out.println(order);
}
sqlSession.close();
}
分部查詢懶加載
<!--延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--當開啟時,任何方法的調用都會加載該對象的所有屬性。否則,每個屬性會按需加載-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--指定哪個對象的方法觸發一次延遲加載。-->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode"/>
添加
添加客戶
<!--保存客戶-->
<insert id="insertCustomer" parameterType="Customer"
useGeneratedKeys="true"
keyColumn="cust_id"
keyProperty="cust_id">
insert into `customer`(cust_name,cust_profession,cust_phone,email)
values (#{cust_name},#{cust_profession},#{cust_phone},#{email})
</insert>
設置關系(外鍵沒有賦值上)
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
Customer customer = new Customer();
customer.setCust_name("新客戶001");
customer.setCust_phone("137090909090");
customer.setEmail("123123@163.com");
customer.setCust_profession("新職業001");
/*先添加客戶 獲取客戶生成的id 再去添加訂單*/
customerMapper.insertCustomer(customer);
Order order = new Order();
order.setOrder_name("新訂單001");
order.setOrder_num("20000001001");
/*設置關系 */
order.setCustomer(customer);
System.out.println(customer);
/*保存訂單*/
orderMapper.insertOrder(order);
sqlSession.commit();
sqlSession.close();
}
添加訂單
<!--保存訂單-->
<!-- useGeneratedKeys="true"把新增加的主鍵賦值到自己定義的keyProperty(id)中 -->
<insert id="insertOrder"
parameterType="Order"
useGeneratedKeys="true"
keyColumn="order_id"
keyProperty="order_id">
insert into `order`(order_name,order_num,cust_id)
values (#{order_name},#{order_num},#{customer.cust_id})
</insert>
OnToMany
查詢
查詢客戶和客戶訂單
sql語句
SELECT * FROM customer as c LEFT JOIN `order` as o on c.cust_id = o.cust_id;
映射
<resultMap id="custMap" type="Customer">
<id column="cust_id" property="cust_id"/>
<result column="cust_name" property="cust_name"/>
<result column="cust_profession" property="cust_profession"/>
<result column="cust_phone" property="cust_phone"/>
<result column="email" property="email"/>
<collection property="orders" ofType="Order">
<id column="order_id" property="order_id"/>
<id column="order_name" property="order_name"/>
<id column="order_num" property="order_num"/>
</collection>
</resultMap>
<!--查詢所有客戶-->
<select id="getAllCustomers" resultMap="custMap">
select* from `customer` as c LEFT JOIN `order` as o ON c.cust_id = o.cust_id;
</select>
測試
@Test
public void test4(){
/*查詢所有客戶*/
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
List<Customer> allCustomers = customerMapper.getAllCustomers();
for (Customer allCustomer : allCustomers) {
System.out.println(allCustomer);
}
sqlSession.close();
}
添加
保存數據
/*保存客戶*/
public void insertCustomer(Customer customer);
<!--保存客戶-->
<insert id="insertCustomer"
parameterType="Customer"
useGeneratedKeys="true"
keyColumn="cust_id"
keyProperty="cust_id">
insert into `customer`(cust_name,cust_profession,cust_phone,email)
values (#{cust_name},#{cust_profession},#{cust_phone},#{email})
</insert>
/*保存訂單*/
public void insertOrder(Order order);
<!--保存訂單-->
<!-- useGeneratedKeys="true"把新增加的主鍵賦值到自己定義的keyProperty(id)中 -->
<insert id="insertOrder"
parameterType="Order"
useGeneratedKeys="true"
keyColumn="order_id"
keyProperty="order_id">
insert into `order`(order_name,order_num,cust_id)
values (#{order_name},#{order_num},#{customer.cust_id})
</insert>
維護外鍵
/*更新與客戶的關系*/
public void updateCustId(@Param("orderId") Integer orderId, @Param("custId") Integer custId);
<!--更新關系-->
<update id="updateCustId">
update `order` set `cust_id`=#{custId} where `order_id`=#{orderId}
</update>
管理關系
@Test
public void test5(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
Customer customer = new Customer();
customer.setCust_name("新客戶");
Order order1 = new Order();
order1.setOrder_name("訂單2");
Order order2 = new Order();
order2.setOrder_name("訂單2");
customer.getOrders().add(order1);
customer.getOrders().add(order2);
/*保存數據*/
customerMapper.insertCustomer(customer);
orderMapper.insertOrder(order1);
orderMapper.insertOrder(order2);
/*更新關系*/
for (Order order : customer.getOrders()) {
orderMapper.updateCustId(order.getOrder_id(),customer.getCust_id());
}
sqlSession.commit();
sqlSession.close();
}
刪除
-
刪除時一定要先打破關系再做刪除操作
示例
/*根據id刪除客戶*/
public void deleteCustomer(Integer id);
<!--根據id刪除客戶-->
<delete id="deleteCustomer">
delete from `customer` where cust_id=#{id}
</delete>
/*打破跟客戶關系*/
public void updateRelationCustomer(Integer custId);
<!--打破跟客戶關系-->
<update id="updateRelationCustomer">
update `order` set cust_id=null where cust_id=#{custId}
</update>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
//一對多刪除之前 要先打破關系
orderMapper.updateRelationCustomer(36);
/*刪除客戶*/
customerMapper.deleteCustomer(36);
sqlSession.commit();
sqlSession.close();
}
ManyToMany
關系表
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Setter@Getter@ToString
public class Teacher {
private Integer teacher_id;
private String teacher_name;
private List<Student> students = new ArrayList<>();
}
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter@Getter @ToString
public class Student {
private Integer stu_id;
private String stu_name;
}
查詢
分步查詢
查詢出指定的老師
public interface TeacherMapper {
/*查詢指定的老師*/
public Teacher getTeacherWithId(Integer id);
}
<!--查詢指定的老師-->
<select id="getTeacherWithId" resultMap="teacherMap2">
SELECT * from teacher WHERE teacher_id = #{id};
</select>
<resultMap id="teacherMap2" type="Teacher">
<id column="teacher_id" property="teacher_id"/>
<result column="teacher_name" property="teacher_name"/>
<collection property="students" ofType="Student"
select="com.le.mapper.StudentMapper.getStuByTeach"
column="teacher_id"/>
</resultMap>
根據老師id查詢出所有學生
public interface StudentMapper {
/*根據老師id查詢學生*/
public List<Student>getStuByTeach(Integer id);
}
<select id="getStuByTeach" resultType="com.le.domain.Student">
SELECT * from student where stu_id in(SELECT stu_id from stu_teacher_rel where teacher_id = #{id})
</select>
查詢
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.openSession();
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = teacherMapper.getTeacherWithId(2);
System.out.println(teacher);
sqlSession.close();
}
左邊接查詢
public interface TeacherMapper {
/*查詢老師 並且把關聯的學生也查出來*/
public List<Teacher>getAllTeachers();
}
<mapper namespace="com.le.mapper.TeacherMapper">
<!-- 查詢老師 並且把關聯的學生也查出來 -->
<resultMap id="teacherMap" type="Teacher">
<id column="teacher_id" property="teacher_id"/>
<result column="teacher_name" property="teacher_name"/>
<collection property="students" javaType="list" ofType="Student">
<id column="stu_id" property="stu_id"/>
<result column="stu_name" property="stu_name"/>
</collection>
</resultMap>
<select id="getAllTeachers" resultMap="teacherMap">
SELECT*FROM teacher as t
LEFT JOIN stu_teacher_rel as r on t.teacher_id = r.teacher_id
LEFT JOIN student as s ON r.stu_id = s.stu_id
</select>
</mapper>
添加
添加老師
/*保存老師*/
public void insertTeacher(Teacher teacher);
<!--保存老師-->
<insert id="insertTeacher" parameterType="Teacher"
useGeneratedKeys="true"
keyProperty="teacher_id"
keyColumn="teacher_id">
insert into`teacher`(teacher_name) values (#{teacher_name})
</insert>
添加學生
/*保存學生*/
public void insertStudent(Student student);
<!--保存學生-->
<insert id="insertStudent" parameterType="Student"
useGeneratedKeys="true"
keyProperty="stu_id"
keyColumn="stu_id">
insert into `student` (stu_name) values (#{stu_name})
</insert>
添加中間關系
/*插入關系表*/
public void insertRelation(@Param("stuId") Integer stuId, @Param("teacherId") Integer teacherId);
<!-- 插入關系表 -->
<insert id="insertRelation">
insert into stu_teacher_rel (stu_id,teacher_id) values (#{stuId},#{teacherId})
</insert>
動態sql
什么是動態sql
通過mybatis提供的各種標簽方法實現動態拼接sql。
if標簽
需求
根據客戶名和職業查詢客戶
/*根基客戶名稱和職業來查詢*/
public List<Customer> getCustomer(@Param("name") String name, @Param("profession") String profession);
<select id="getCustomer"
resultType="com.le.domain.Customer">
select from customer where cust_name=#{name} ad cust_profession=#{profession}
</select>
存在問題
- 有可能傳入的名稱或級別為空
- 可以使用if標簽來進行判斷
- 如果前一個條件后面多一個and執行就會報錯(也就是說需要將and分布合理)
<!--if標簽:符合條件會自動把if中的內容拼接到sql之后-->
<select id="getCustomer" resultType="com.le.domain.Customer">
select * from `customer` where
<if test="name != null and name != ''">
`cust_name`=#{name}
</if>
<if test="profession != null and profession!=''">
and `cust_profession`=#{profession}
</if>
</select>
Where標簽
- 去掉第一個前And
<!--where標簽:會自動生成和刪除where
還能刪除where后第1個and
條件前and去掉 -->
<select id="getCustomer2"
resultType="com.le.domain.Customer">
select * from `customer`
<where>
<if test="name != null and name != ''">
and `cust_name`=#{name}
</if>
<if test="profession != null and profession!=''">
and `cust_profession`=#{profession}
</if>
</where>
</select>
trim標簽
<!--trim標簽:
prefix:設置前綴 在第一個條件之前加一個前綴
prefixOverrides: 條件前綴覆蓋 把第一個條件之前的and變成空
suffix: 設置后綴 在最后一個條件之后加一個后綴
suffixOverrides: 條件后綴覆蓋 把最后一個條件之后的and變成空
-->
<select id="getCustomer3"
resultType="com.le.domain.Customer">
select * from `customer`
<trim prefix="where" prefixOverrides="and" suffixOverrides="and" >
<if test="name != null and name != ''">
and `cust_name`=#{name} and
</if>
<if test="profession != null and profession!=''">
`cust_profession`=#{profession} and
</if>
</trim>
</select>
choose標簽
<!--
choose 只要第一個條件滿足,后面條件都不執行
when
otherwise
-->
<select id="getCustomer" resultType="com.le.domain.Customer">
<include refid="selectID"/>
<where>
<choose>
<when test="profession != null and profession!=''">
`cust_profession`=#{profession}
</when>
<when test="name != null and name != ''">
`cust_name`=#{name}
</when>
<otherwise>1=1</otherwise>
</choose>
</where>
</select>
set標簽
<!--
set標簽:會添加update 中 set 並且它會把最后一個,去掉
-->
<!--更新客戶-->
<update id="updateCustomer">
update `customer`
<set>
<if test="cust_name != null and cust_name !='' ">
cust_name=#{cust_name},
</if>
<if test="cust_profession != null and cust_profession !='' ">
cust_profession=#{cust_profession},
</if>
</set>
where cust_id=#{cust_id}
</update>
foreach標簽
查詢條件值為指定的值當中
<select id="queryCustomerIn"
resultType="com.le.domain.Customer">
<include refid="selectID"/>where `cust_id` in(2,3,5,6);
</select>
<!--注意 在include當中定義的property 取的時候 要使用${} -->
<sql id="selectID">
<choose>
<when test="${lk} == 2">
select cust_name from `customer`
</when>
<otherwise>
select * from `customer`
</otherwise>
</choose>
</sql>
給定的值可以以三種形式給出
數組
/*根據id查詢指定的客戶 多個客戶*/
public List<Customer> getCustomers(Integer[] array);
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Integer[] ids = new Integer[4]{2,3,5,6};
List<Customer> customers = customerMapper.getCustomers(ids);
for (Customer customer : customers) {
System.out.println(customer);
}
sqlSession.close();
}
<select id="getCustomers"
parameterType="Integer[]" resultType="com.le.domain.Customer">
select * from `customer` where `cust_id` in
<foreach collection="array" open="(" close=")" separator="," item="item">
#{item}
</foreach>
</select>
List
<select id="getCustomers"
parameterType="List" resultType="com.le.domain.Customer">
select * from `customer` where `cust_id` in
<foreach collection="list" open="(" close=")" separator="," item="item">
#{item}
</foreach>
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
ArrayList<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
list.add(6);
List<Customer> customers = customerMapper.getCustomers(list);
for (Customer customer : customers) {
System.out.println(customer);
}
sqlSession.close();
}
包裝類VO
創建Vo
package com.le.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Setter@Getter@ToString
public class QueryVo {
private Integer[] ids;
private List<Integer> idList;
}
測試
<select id="getCustomers"
parameterType="QueryVo" resultType="com.le.domain.Customer">
<include refid="selectID"/> where `cust_id` in
<foreach collection="idList" open="(" close=")" separator="," item="ids">
#{ids}
</foreach>
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
QueryVo queryVo = new QueryVo();
queryVo.setIds(new Integer[]{2,3,4,5});
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(6);
queryVo.setIdList(arrayList);
List<Customer> customers = customerMapper.getCustomers(queryVo);
for (Customer customer : customers) {
System.out.println(customer);
}
sqlSession.close();
}
bind標簽
<!--
bind標簽:可以取出傳入的值,重新處理, 賦值給別外一個值
-->
<select id="getCustomerWithId" resultType="Customer">
<bind name="newId" value="id+2"/>
<include refid="selectID">
<property name="lk" value="2"/>
</include> where cust_id=#{newId}
</select>
Sql片段
- Sql中可將重復的sql提取出來,使用時用include引用即可,最終達到sql重用的目的。
<select id="getCustomerWithId" resultType="Customer">
<include refid="selectID">
<property name="lk" value="2"/>
</include> where cust_id=#{id}
</select>
<!--注意 在include當中定義的property 取的時候 要使用${} -->
<sql id="selectID">
<choose>
<when test="${lk} == 2">
select cust_name from `customer`
</when>
<otherwise>
select * from `customer`
</otherwise>
</choose>
</sql>
緩存
一級緩存
緩存介紹
- MyBatis中使用緩存來提高其性能。
- 當查詢數據時, 會先從緩存中取出數據,如果緩存中沒有,再到數據庫當中查詢
- MyBatis中的緩存分為兩種:一級緩存和二級緩存
- 一級緩存是sqlSession級別的,二級緩存是mapper級別的
一級緩存
- 本地緩存 (默認開啟)
- 在sqlSession沒有關閉之前,再去查詢時, 會從緩存當中取出數據,不會重新發送新的sql
一級緩存失效
- 如果在查詢之前,執行了增\刪\改 緩存就會失效
- 手動清空緩存
- 如果兩次的查詢條件不一樣,緩存也會失效
- 如果兩個查詢在不同的sqlsession當中
二級緩存
二級緩存介紹
全局作用域緩存 一個namespace對應一個緩存
如果會話關閉,一級緩存的數據會被保存到二級緩存中
不同namespace查出的數據 ,會放到自己對應的緩存中
現在默認也是打開的
二級緩存使用步驟
1.確保在配置文件當中開啟二級緩存
2.在對應的mapper中添加cache標簽
eviction
回收策略
flushInterval
刷新間隔
默認不清空
readOnly
是否只讀
true
告訴Mybatis是只讀操作,不去修改數據
Mybatis為了加快獲取速度,會直接將緩存的引用將給用, 不安全, 速度快
false
非只讀,有可能修改數據
Mybatis會利用序列化和反序列化復制一份給你 速度慢些
size
可以存放多少個元素
type
可以用來指定自定義的緩存
3.POJO需要實現Serializable接口
注意事項
- 查詢的數據都會先放到一級緩存當中
- 只有會話關閉,一級緩存中的數據才會轉稱到二級緩存中
緩存相關屬性
cacheEnabled
只能控制二級緩存的開關
select中useCache
控制的也是二級緩存是否使用
增刪改標簽中flushCache
一級和二級都會被清空
增刪改flushCache默認為true
查詢flushCache默認為false
sqlSession.clearCache()
只清除當前session的一級緩存
localCacheScope
本地緩存作用域
取值
SESSION
STATEMENT
STATEMENT可以使用它禁用緩存
緩存使用順序
- 先到二級緩存當中查找
- 如果二級緩存中沒有,就去找一級緩存
- 如果一級緩存中也沒有就去到數據庫當中查詢