第一個mybatis程序
1、安裝mysql
2、安裝SQLyog
3、建表
CREATE DATABASE `mybatis`;
`user``user``user`
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`(`id`,`name`,`pwd`) VALUES
(1,'李一','123456'),
(2,'王二','654321')
4、新建maven(maven版本3.6.1)項目
4.1、刪除src目錄,作爲一個父工程
4.2、導入依賴
<?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.yk</groupId>
<artifactId>mybatis</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!--子模塊-->
<modules>
<module>mybatis-01</module>
</modules>
<dependencies>
<!--mybatis包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!--Maven靜態資源過濾-->
<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>
</project>
maven靜態資源過濾的作用:
maven由於他的約定大於配置,遇到配置文件無法生效或者導出時的解決方方法
在build中配置的resources來防止資源導出失敗
4.3、在項目右鍵-new-module,新建一個名爲mybatis-01的模塊
這樣的好處是子模塊不用重新導入jar包不用每次導包,因爲父項目已經導入了。
子模塊pom文件加入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">
<parent>
<artifactId>mybatis</artifactId>
<groupId>com.yk</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatis-01</artifactId>
<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>
</project>
4.4、編寫mybatis配置文件
主要寫連接數據庫的配置和需要註冊的接口xml文件
在mybatis-01下的resources下創建mybatis-config.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>
<!--可以配置多個環境,默認是development-->
<environments default="development">
<!--環境1,名爲development-->
<environment id="development">
<!--事務管理類型-->
<transactionManager type="JDBC"/>
<!-- 配置數據源,採用mybatis連接池 -->
<dataSource type="POOLED">
<!--驅動-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--每個mapper.xml文件都需要在mybatis核心配置文件中註冊-->
<mappers>
<mapper resource="com/yk/dao/userMapper.xml"/>
</mappers>
</configuration>
4.5編寫mybatias工具類
封裝創建mubatis連接的方法
新建MybatisUtils.class文件
package com.yk.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 {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用mybatis第一步,獲取SqlSessionFactory對象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。
// SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句
//獲取SqlSession連接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
編寫代碼
實體類
package 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 + '\'' +
'}';
}
}
**接口類UserMapper.**interface
這個接口提供方法給外部調用,實際執行的代碼在 UserMapper.xml 中,
import com.yk.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
}
實現類UserMapper.xml(接口實現類由原來的UserDaoImpl轉變爲mapper配置文件,跟接口一起放在Dao)
調用UserMapper.interface的方法執行xml裏面的sql語句並返回結果
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=綁定一個對於的dao/mapper接口-->
<mapper namespace="com.yk.dao.UserMapper">
<!--select查詢語句-->
<select id="getUserList" resultType="com.yk.pojo.User">
select * from mybatis.user
</select>
</mapper>
測試類
import com.yk.pojo.User;
import com.yk.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test
import java.util.List;
public class MyTest {
@Test
public void selectUser() {
SqlSession session = MybatisUtils.getSession();
//方法一:老方法,不推薦使用
//List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
//方法二:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUserList();
for (User user: users){
System.out.println(user);
}
session.close();
}
}
查
根據ID查詢
UserMapper.interface
public interface UserMapper {
List<User> getUserList();
//通過ID
User selectUserById(int id);
}
UserMapper.xml
<select id="selectUserById" resultType="com.yk.pojo.User">
select * from user where id=#{id}
</select>
測試
UserMapper mapper = session.getMapper(UserMapper.class);
User userById = mapper.selectUserById(1);
根據 密碼 和 名字 查詢用戶
1、在接口方法的參數前加 @Param屬性(方法有多個參數,必須加上 @Param)
2、Sql語句編寫的時候,直接取@Param中設置的值即可,不需要單獨設置參數類型
User selectUserByNP(@Param("username") String username, @Param("pwd") String pwd);
<select id="selectUserByNP" resultType="com.yk.pojo.User">
select * from user where name = #{username} and pwd = #{pwd}
</select>
User liyi = mapper.selectUserByNP("李一", "123456");
使用萬能的Map
1、在接口方法中,參數直接傳遞Map;
User selectUserByNP2(Map<String,Object> map);
2、編寫sql語句的時候,需要傳遞參數類型,參數類型爲map
<select id="selectUserByNP2" parameterType="map" resultType="com.yk.pojo.User">
select * from user where name = #{username} and pwd = #{pwd}
</select>
3、在使用方法的時候,Map的 key 爲 sql中取的值即可,沒有順序要求!
Map<String, Object> map = new HashMap<String, Object>();
map.put("username","李一");
map.put("pwd","123456");
User user = mapper.selectUserByNP2(map);
總結:如果參數過多,我們可以考慮直接使用Map實現,如果參數比較少,直接傳遞參數即可
增 insert
我們一般使用insert標籤進行插入操作,它的配置和select標籤差不多!
給數據庫增加一個用戶
int addUser(User user);
<insert id="addUser" parameterType="com.yk.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
User user = new User(5,"王五","zxcvbn");
int i = mapper.addUser(user);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
session.close();
注意點:增、刪、改操作需要提交事務!
改 update
我們一般使用update標籤進行更新操作,它的配置和select標籤差不多!
int updateUser(User user);
<update id="updateUser" parameterType="com.yk.pojo.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
User user = mapper.selectUserById(1);
user.setPwd("asdfgh");
int i = mapper.updateUser(user);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
刪 delete
我們一般使用delete標籤進行刪除操作,它的配置和select標籤差不多!
int deleteUser(int id);
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
int i = mapper.deleteUser(2);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
小結:
-
所有的增刪改操作都需要提交事務!
-
接口所有的普通參數,儘量都寫上@Param參數,尤其是多個參數時,必須寫上!
-
有時候根據業務的需求,可以考慮使用map傳遞參數!
-
爲了規範操作,在SQL的配置文件中,我們儘量將Parameter參數和resultType都寫上!
模糊查詢like語句該怎麼寫?
相當於關鍵詞查詢
第1種:在Java代碼中添加sql通配符。
List<User> getUserLike(String value);
<select id="getUserLike" resultType="com.yk.pojo.User">
select * from user where name like #{value}
</select>
List<User> userLike = mapper.getUserLike("%李%");
//會輸出含有李的信息
第2種:在sql語句中拼接通配符,會引起sql注入
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>
配置解析
核心配置文件
-
mybatis-config.xml 系統核心配置文件
-
MyBatis 的配置文件包含了會深深影響 MyBatis 行爲的設置和屬性信息。
-
能配置的內容如下:
configuration(配置)
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)
mappers(映射器)
<!-- 注意元素節點的順序!順序不對會報錯 -->
我們可以閱讀 mybatis-config.xml 上面的dtd的頭文件!
environments元素
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
-
配置MyBatis的多套運行環境,將SQL映射到多個不同的數據庫上,必須指定其中一個爲默認運行環境(通過default指定)
-
子元素節點:environment
-
dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 連接對象的資源。
-
數據源是必須配置的。
-
有三種內建的數據源類型
-
type="[UNPOOLED|POOLED|JNDI]")
-
unpooled:這個數據源的實現只是每次被請求時打開和關閉連接。
-
pooled:這種數據源的實現利用“池”的概念將 JDBC 連接對象組織起來 , 這是一種使得併發 Web 應用快速響應請求的流行處理方式。
-
jndi:這個數據源的實現是爲了能在如 Spring 或應用服務器這類容器中使用,容器可以集中或在外部配置數據源,然後放置一個 JNDI 上下文的引用。
-
數據源也有很多第三方的實現,比如dbcp,c3p0,druid等等....
-
這兩種事務管理器類型都不需要設置任何屬性。
-
具體的一套環境,通過設置id進行區別,id保證唯一!
-
子元素節點:transactionManager - [ 事務管理器 ]
<!-- 語法 -->
<transactionManager type="[ JDBC | MANAGED ]"/>
mappers元素
mappers
-
映射器 : 定義映射SQL語句文件
-
既然 MyBatis 的行爲其他元素已經配置完了,我們現在就要定義 SQL 映射語句了。但是首先我們需要告訴 MyBatis 到哪裏去找到這些語句。Java 在自動查找這方面沒有提供一個很好的方法,所以最佳的方式是告訴 MyBatis 到哪裏去找映射文件。你可以使用相對於類路徑的資源引用, 或完全限定資源定位符(包括
file:///
的 URL),或類名和包名等。映射器是MyBatis中最核心的組件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL語句都必須在xml文件中配置。而從MyBatis 3開始,還支持接口映射器,這種映射器方式允許以Java代碼的方式註解定義SQL語句,非常簡潔。
引入資源方式
<!-- 使用相對於類路徑的資源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定資源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!--
使用映射器接口實現類的完全限定類名
需要配置文件名稱和接口名稱一致,並且位於同一目錄下
-->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--
將包內的映射器接口實現全部註冊爲映射器
但是需要配置文件名稱和接口名稱一致,並且位於同一目錄下
-->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
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">
<mapper namespace="com.kuang.mapper.UserMapper">
</mapper>
-
namespace中文意思:命名空間,作用如下:
-
namespace的命名必須跟某個接口同名
-
接口中的方法與映射文件中sql語句id應該一一對應
-
namespace和子元素的id聯合保證唯一 , 區別不同的mapper
-
綁定DAO接口
-
namespace命名規則 : 包名+類名
-
MyBatis 的真正強大在於它的映射語句,這是它的魔力所在。由於它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 爲聚焦於 SQL 而構建,以儘可能地爲你減少麻煩。
Properties優化
數據庫這些屬性都是可外部配置且可動態替換的,既可以在典型的 Java 屬性文件中配置,亦可通過 properties 元素的子元素來傳遞。具體的官方文檔
我們來優化我們的配置文件
第一步 ; 在資源目錄下新建一個db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
第二步 : 將文件導入properties 配置文件
<configuration>
<!--導入properties文件-->
<properties resource="db.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>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
typeAliases優化
類型別名是爲 Java 類型設置一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減少類完全限定名的冗餘。
意思就是將com.kuang.pojo.User自定義名爲User,可以通過User代替
<!--配置別名,注意順序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
當這樣配置時,User
可以用在任何使用com.kuang.pojo.User
的地方。
也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
每一個在包 com.kuang.pojo
中的 Java Bean,在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作爲它的別名。
意思是將整個包設置類型別名,使用是跟包內的class文件名一樣,首字母小寫
若有註解,則別名爲其註解值。見下面的例子:
也可以用註解@Alias設置別名
@Alias("user")
public class User {
...
}
設置
-
設置(settings)相關
-
懶加載 lazyLoadingEnabled
-
日誌實現 autoMappingUnknownColumnBehavior
-
緩存開啓關閉 cacheEnabled
-
-
一個配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
類型處理器
-
無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
-
你可以重寫類型處理器或創建你自己的類型處理器來處理不支持的或非標準的類型。【瞭解即可】
對象工廠
-
MyBatis 每次創建結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成。
-
默認的對象工廠需要做的僅僅是實例化目標類,要麼通過默認構造方法,要麼在參數映射存在的時候通過有參構造方法來實例化。
-
如果想覆蓋對象工廠的默認行爲,則可以通過創建自己的對象工廠來實現。【瞭解即可】
作用域(Scope)和生命週期
理解我們目前已經討論過的不同作用域和生命週期類是至關重要的,因爲錯誤的使用會導致非常嚴重的併發問題。
我們可以先畫一個流程圖,分析一下Mybatis的執行過程!
作用域理解
-
SqlSessionFactoryBuilder 的作用在於創建 SqlSessionFactory,創建成功後,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在於創建 SqlSessionFactory 的方法中,而不要讓其長期存在。因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。
-
SqlSessionFactory 可以被認爲是一個數據庫連接池,它的作用是創建 SqlSession 接口對象。因爲 MyBatis 的本質就是 Java 對數據庫的操作,所以 SqlSessionFactory 的生命週期存在於整個 MyBatis 的應用之中,所以一旦創建了 SqlSessionFactory,就要長期保存它,直至不再使用 MyBatis 應用,所以可以認爲 SqlSessionFactory 的生命週期就等同於 MyBatis 的應用週期。
-
由於 SqlSessionFactory 是一個對數據庫的連接池,所以它佔據着數據庫的連接資源。如果創建多個 SqlSessionFactory,那麼就存在多個數據庫連接池,這樣不利於對數據庫資源的控制,也會導致數據庫連接資源被消耗光,出現系統宕機等情況,所以儘量避免發生這樣的情況。
-
因此在一般的應用中我們往往希望 SqlSessionFactory 作爲一個單例,讓它在應用中被共享。所以說 SqlSessionFactory 的最佳作用域是應用作用域。
-
如果說 SqlSessionFactory 相當於數據庫連接池,那麼 SqlSession 就相當於一個數據庫連接(Connection 對象),你可以在一個事務裏面執行多條 SQL,然後通過它的 commit、rollback 等方法,提交或者回滾事務。所以它應該存活在一個業務請求中,處理完整個請求後,應該關閉這條連接,讓它歸還給 SqlSessionFactory,否則數據庫資源就很快被耗費精光,系統就會癱瘓,所以用 try...catch...finally... 語句來保證其正確關閉。
-
所以 SqlSession 的最佳的作用域是請求或方法作用域。
要解決的問題:屬性名和字段名不一致
數據庫字段爲pwd
實體類字段爲
privateStringpassword; //密碼和數據庫不一樣!
- 查出來的結果:User{id=1, name='狂神', password='null'}
分析:
-
select * from user where id = #{id} 可以看做
select id,name,pwd from user where id = #{id}
-
mybatis會根據這些查詢的列名(會將列名轉化爲小寫,數據庫不區分大小寫) , 去對應的實體類中查找相應列名的set方法設值 , 由於找不到setPwd() , 所以password返回null ; 【自動映射】
解決方案
方案一:爲列名指定別名 , 別名和java實體類的屬性名一致 .(更改sql語句)
<select id="selectUserById" resultType="User">
select id , name , pwd as password from user where id = #{id}
</select>
方案二:使用結果集映射->ResultMap 【推薦】(在UserMapper.xml中添加)
如果實體類字段跟數據庫字段不同可以使用resultmap屬性解決
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
<resultMap id="UserMap" type="User">
<!-- id爲主鍵 -->
<id column="id" property="id"/>
<!-- column是數據庫表的列名 , property是對應實體類的屬性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
ResultMap
自動映射(默認是自動的)
-
resultMap
元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBCResultSets
數據提取代碼中解放出來。 -
實際上,在爲一些比如連接的複雜語句編寫映射代碼的時候,一份
resultMap
能夠代替實現同等功能的長達數千行的代碼。 -
ResultMap 的設計思想是,對於簡單的語句根本不需要配置顯式的結果映射,而對於複雜一點的語句只需要描述它們的關係就行了。
你已經見過簡單映射語句的示例了,但並沒有顯式指定 resultMap
。比如:
<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
</select>
上述語句只是簡單地將所有的列映射到 HashMap
的鍵上,這由 resultType
屬性指定。雖然在大部分情況下都夠用,但是 HashMap 不是一個很好的模型。你的程序更可能會使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 對象)作爲模型。
ResultMap
最優秀的地方在於,雖然你已經對它相當瞭解了,但是根本就不需要顯式地用到他們。
手動映射
1、返回值類型爲resultMap
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
2、編寫resultMap,實現手動映射!
<resultMap id="UserMap" type="User">
<!-- id爲主鍵 -->
<id column="id" property="id"/>
<!-- column是數據庫表的列名 , property是對應實體類的屬性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
日誌工廠
思考:我們在測試SQL的時候,要是能夠在控制檯輸出 SQL 的話,是不是就能夠有更快的排錯效率?
如果一個 數據庫相關的操作出現了問題,我們可以根據輸出的SQL語句快速排查問題。
對於以往的開發過程,我們會經常使用到debug模式來調節,跟蹤我們的代碼執行過程。但是現在使用Mybatis是基於接口,配置文件的源代碼執行過程。因此,我們必須選擇日誌工具來作爲我們開發,調節程序的工具。
Mybatis內置的日誌工廠提供日誌功能,具體的日誌實現有以下幾種工具:
-
SLF4J
-
Apache Commons Logging
-
Log4j 2
-
Log4j
-
JDK logging
具體選擇哪個日誌實現工具由MyBatis的內置日誌工廠確定。它會使用最先找到的(按上文列舉的順序查找)。如果一個都未找到,日誌功能就會被禁用。
標準日誌實現(不用額外導包)
指定 MyBatis 應該使用哪個日誌記錄實現。如果此設置不存在,則會自動發現日誌記錄實現。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
簡介:
-
Log4j是Apache的一個開源項目
-
通過使用Log4j,我們可以控制日誌信息輸送的目的地:控制檯,文本,GUI組件....
-
我們也可以控制每一條日誌的輸出格式;
-
通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程。最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。
使用步驟:
1、導入log4j的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、配置文件編寫
在resources文件下新建 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/kuang.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、setting設置日誌實現
在mybatis-config.xml添加
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、在程序中使用Log4j進行輸出!
//注意導包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void selectUser() {
logger.info("info:進入selectUser方法");
logger.debug("debug:進入selectUser方法");
logger.error("error: 進入selectUser方法");
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
limit實現分頁
思考:爲什麼需要分頁?
在學習mybatis等持久層框架的時候,會經常對數據進行增刪改查操作,使用最多的是對數據庫進行查詢操作,如果查詢大量數據的時候,我們往往使用分頁進行查詢,也就是每次處理小部分數據,這樣對數據庫壓力就在可控範圍內。
使用Limit實現分頁
#語法
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 檢索記錄行 6-15
#爲了檢索從某一個偏移量到記錄集的結束所有的記錄行,可以指定第二個參數爲 -1:
SELECT * FROM table LIMIT 95,-1; // 檢索記錄行 96-last.
#如果只給定一個參數,它表示返回最大的記錄行數目:
SELECT * FROM table LIMIT 5; //檢索前 5 個記錄行
#換句話說,LIMIT n 等價於 LIMIT 0,n。
步驟:
1、修改Mapper文件
<select id="selectUser" parameterType="map" resultType="com.yk.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>
2、Mapper接口,參數爲map
//選擇全部用戶實現分頁
List<User> selectUser(Map<String,Integer> map);
3、在測試類中傳入參數測試
- 推斷:起始位置 = (當前頁面 - 1 ) * 頁面大小
//分頁查詢 , 兩個參數startIndex , pageSize
@Test
public void testSelectUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int currentPage = 1; //第幾頁
int pageSize = 2; //每頁顯示幾個
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",(currentPage-1)*pageSize);
map.put("pageSize",pageSize);
List<User> users = mapper.selectUser(map);
for (User user: users){
System.out.println(user);
}
session.close();
}
RowBounds分頁
我們除了使用Limit在SQL層面實現分頁,也可以使用RowBounds在Java代碼層面實現分頁,當然此種方式作爲了解即可。我們來看下如何實現的!
步驟:
1、mapper接口
//選擇全部用戶RowBounds實現分頁
List<User> getUserByRowBounds();
2、mapper文件
<select id="getUserByRowBounds" resultType="user">
select * from user
</select>
3、測試類
在這裏,我們需要使用RowBounds類
@Test
public void testUserByRowBounds() {
SqlSession session = MybatisUtils.getSession();
int currentPage = 2; //第幾頁
int pageSize = 2; //每頁顯示幾個
RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
//通過session.**方法進行傳遞rowBounds,[此種方式現在已經不推薦使用了]
List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user: users){
System.out.println(user);
}
session.close();
}
mybatis分頁插件 PageHelper
瞭解即可,可以自己嘗試使用
官方文檔:https://pagehelper.github.io/
註解
官網解釋
//接口類方法
@Select("select * from user where id=#{id}")
user selectUserById(int id);
//MybatisConfig.xml註冊
<mappers>
<mapper resource="dao/userMapper.xml"/>
</mappers>
使用註解的好處是可以不用寫接口實現類的xml。
本質:反射機制實現
底層:動態代理
註解實現增刪改查
//查
@Select("select * from user where id=#{id}")
user SelectUserById(int id);
//增
@Insert("insert into user(id,name,pwd) value (#{id},#{name},#{pwd})" )
int addUser(user user);
//改
@Update("updata user set name=#{name},pwd=#{pwd} where id =#{id}")
int UpdataUser(user user);
//刪
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int id);
lombok插件
lombok通過註解的方式使我們的實體類可以不寫構造方法、get、set、tostring等
1、idea-file-settings-plugins-搜索lombok-install
2、添加lombok依賴
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
<scope>provided</scope>
</dependency>
示例
//Data自動添加無參構造、GET、SET、TOSTRING、hashcode,有參構造不會被添加
@Data
//添加有參構造器,但有參會失效
@AllArgsConstructor
//添加無參構造器
@NoArgsConstructor
public class user {
private int id;
private String name;
private String pwd;
}
複雜拆查詢
環境搭建
CREATE TABLE `teacher`(
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`,`name`) VALUES (1,'葉老師')
CREATE TABLE `student`(
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
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');
兩個接口
public interface StudentMapper {
}
public interface TeacherMapper {
@Select("select * from teacher where id=#{tid}")
Teacher getTeacher(@Param("tid") int id);
}
兩個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">
<mapper namespace="com.yk.dao.StudentMapper">
</mapper>
<?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">
<mapper namespace="com.yk.dao.TeacherMapper">
</mapper>
兩個實體類
@Data
public class Student {
private int id;
private String name;
//關聯另外一個表的數據
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
}
在配置文件中註冊xml報找不到資源錯,pom文件下加了xml的maven過濾器也不行,沒有找到具體原因
<mappers>
<!-- 報錯,找不到資源-->
<mapper resource="com.yk.dao.TeacherMapper"/>
<mapper resource="com/yk/dao/TeacherMapper"/>
<!-- 方法一,resource改class纔可以-->
<mapper class="com.yk.dao.TeacherMapper"/>
<!-- 方法二,將整個包的xml註冊-->
<package name="com.yk.dao"/>
</mappers>
多對一
我們希望通過查詢學生的信息並得到關聯老師,
user表的字段是tid,實體類字段改成關聯的teacher,直接查詢學生,關聯的teacher是null
方法一
<!-- 思路:-->
<!-- 1、查詢所以學生信息-->
<!-- 2、根據查到的學生tid再次查找對應的老師,子查詢-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="com.yk.pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 複雜屬性需要單獨處理,對象:association,集合collection-->
<association property="teacher" column="tid" javaType="com.yk.pojo.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.yk.pojo.Teacher">
select * from teacher where id=#{tid}
</select>
方法二(推薦)
<!-- 按照結果嵌套處理-->
<select id="getStudent" resultMap="StudentTeacher">
SELECT s.id sid,s.name sname,t.id tid,t.name tname
from student s,teacher t
where s.tid=t.id;
</select>
<resultMap id="StudentTeacher" type="com.yk.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.yk.pojo.Teacher" >
<result property="name" column="tname"/>
<result property="id" column="tid"/>
</association>
</resultMap>
方法三(sql)
select s.id,s.name from student s,teacher t where s.tid=t.id;
一對多
通過老師信息查詢這個老師的所以學生
兩個實體類
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
方法一(sql)
SELECT s.id sid,s.name sname,t.id tid,t.name tname
FROM student s,teacher t
WHERE s.tid=t.id AND t.id=1
方法二
<!-- 按結果嵌套查詢-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.id tid,t.name tname
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="com.yk.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- 複雜屬性需要單獨處理,對象:association,集合collection-->
<!-- javaType指定屬性的類型,集合中的泛型用ofType獲取-->
<collection property="students" ofType="com.yk.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
方法三
<!--子查詢-->
<select id="getTeacher" resultMap="TeacherStudent">
select * from teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent" type="com.yk.pojo.Teacher">
<collection property="students" column="id" javaType="ArrayList"
ofType="com.yk.pojo.Student" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="com.yk.pojo.Student">
select * from student where tid=#{tid}
</select>
動態sql
sql查詢(where 1=1是用於拼接條件,沒有實際用處)
//查詢全部
SELECT * FROM blog WHERE 1=1
//拼接條件
SELECT * FROM blog WHERE 1=1 and title="666"
if
調用queryBlogIf方法不傳參查詢全部,如果傳入title參數會拼接if條件的sql
<select id="queryBlogIf" parameterType="map" resultType="blog">
SELECT * FROM blog
<where>
<if test="title!=null">
title=#{title}
</if>
-- 如果上面那個sql沒有拼接,此處拼接的sql會自動刪除and,否則sql會報錯
<if test="author!=null">
and author=#{author}
</if>
</where>
</select>
choose、when、otherwise
只會執行滿足條件的一個,類似switch
<select id="queryBlogChoose" parameterType="map" resultType="blog">
SELECT * FROM blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
and author=#{author}
</when>
<otherwise>
and view=#{view}
</otherwise>
</choose>
</where>
</select>
trim、where、set
<update id="updataBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
and author=#{author}
</if>
</set>
where id=#{id}
</update>
sql片段
將重複使用的sql提取出來
<sql id="if-t-a">
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
and author=#{author}
</if>
</sql>
使用include引入
<select id="queryBlogIf" parameterType="map" resultType="blog">
SELECT * FROM blog
<where>
<include refid="if-t-a"></include>
</where>
</select>
foreach
sql
SELECT * FROM blog WHERE 1=1 AND (id=1 OR id=2 OR id=3)
<select id="queryBlogForeach" parameterType="map" resultType="blog">
SELECT * FROM blog
<where>
-- open開始截取,close結束截取,separator中間截取
<foreach collection="ids" item="id" open="and(" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
測試類
HashMap hashMap = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
hashMap.put("ids",ids);
mapper.queryBlogForeach(hashMap);
自定義Ehcache緩存
百度