1. 程式人生 > 實用技巧 >MyBatis狂神總結筆記

MyBatis狂神總結筆記

MyBatis狂神總結筆記

簡介

自學的【狂神JAVA】MyBatis

GitHub原始碼: https://github.com/Donkequan/Mybatis-Study

分享自寫原始碼和筆記

配置用的

  • jdk13.0.2 (jdk1.7以上均可)

  • Maven 3.6.3

  • MySQL 5.7 (mysql5.6以上均可)

1. 配置

官網文件: https://mybatis.org/mybatis-3/zh/getting-started.html

pom.xml

<?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>batis</groupId>
<artifactId>batis-maven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-01</module>
</modules>

<!--匯入依賴-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>

src/main/resources

<?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>
<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://111.230.212.103:3306/mybatis?userSSL=true&amp;
userUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="hdk123"/>
</dataSource>
</environment>
</environments>
</configuration>

src/main/java

package com.hou.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;

//sqlSessionFactory --> sqlSession
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 語句

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}

}

編寫程式碼

  • 實體類

    src/main/java

package com.hou.pogo;

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.hou.dao;

import com.hou.pogo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}
  • 介面實現類

<?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繫結一個對應的mapper介面-->
<mapper namespace="com.hou.dao.UserDao">

<!--id方法名-->
<select id="getUserList" resultType="com.hou.pogo.User">
select * from mybatis.user
</select>

</mapper>

測試

注意點:

org.apache.ibatis.binding.BindingException: Type interface com.hou.dao.UserDao is not known to the MapperRegistry.

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>
<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://111.230.212.103:3306/mybatis?userSSL=true&amp;
userUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="hdk123"/>
</dataSource>
</environment>
</environments>

<!--每一個mapper.xml都需要註冊-->
<mappers>
<mapper resource="com/hou/dao/UserMapper.xml"/>
</mappers>

</configuration>

在兩個pom.xml中加入

<!--在build中配置resources,來防止我們資源匯出失敗的問題-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

步驟

  1. 匯入包

  2. 配置資料庫

  3. 建造工具類

SqlSessionFactoryBuilder

這個類可以被例項化、使用和丟棄,一旦建立了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 例項的最佳作用域是方法作用域(也就是區域性方法變數)。 你可以重用 SqlSessionFactoryBuilder 來建立多個 SqlSessionFactory 例項,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立另一個例項。 使用 SqlSessionFactory 的最佳實踐是在應用執行期間不要重複建立多次,多次重建 SqlSessionFactory 被視為一種程式碼“壞習慣”。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。

SqlSession

每個執行緒都應該有它自己的 SqlSession 例項。SqlSession 的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 例項的引用放在一個類的靜態域,甚至一個類的例項變數也不行。 也絕不能將 SqlSession 例項的引用放在任何型別的託管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以開啟一個 SqlSession,返回一個響應後,就關閉它。 這個關閉操作很重要,為了確保每次都能執行關閉操作,你應該把這個關閉操作放到 finally 塊中。 下面的示例就是一個確保 SqlSession 關閉的標準模式

2. 增刪改查

1. namespace

namespace中的包名要和介面一致

2. select

  • id:就是對應的namespace的方法名

  • resultType:sql語句的返回值!

  • parameterType: 引數型別!

  1. 編寫介面

  2. 編寫對應的mapper中的對應語句

  3. 測試

UserMapper

package com.hou.dao;

import com.hou.pogo.User;

import java.util.List;

public interface UserMapper {
//查詢全部使用者
List<User> getUserList();

//根據id查詢使用者
User getUserById(int id);

//插入使用者
void addUser(User user);

//修改使用者
int updateUser(User user);

//刪除使用者
int deleteUser(int id);
}

UserMapper.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繫結一個對應的mapper介面-->
<mapper namespace="com.hou.dao.UserMapper">

<!--id方法名-->
<select id="getUserList" resultType="com.hou.pogo.User">
select * from mybatis.user
</select>

<select id="getUserById" resultType="com.hou.pogo.User"
parameterType="int">
select * from mybatis.user where id = #{id}
</select>

<!--物件中的屬性可以直接取出來-->
<insert id="addUser" parameterType="com.hou.pogo.User">
insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
</insert>

<update id="updateUser" parameterType="com.hou.pogo.User">
update mybatis.user set name=#{name}, pwd=#{pwd} where id =#{id};
</update>

<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id};
</delete>

</mapper>

Test

package com.hou.dao;

import com.hou.pogo.User;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {

@Test
public void test(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();

try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUserList();

// method 2
// List<User> userList = sqlSession.selectList("com.hou.dao.UserDao.getUserList");

for(User user: userList){
System.out.println(user);
}

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}

}

@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}

//增刪改需要提交事務
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(5,"hou","123456"));

//提交事務
sqlSession.commit();
sqlSession.close();
}

@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"hou","123"));

//提交事務
sqlSession.commit();
sqlSession.close();
}

@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(5);

//提交事務
sqlSession.commit();
sqlSession.close();
}
}

注意點:增刪改需要提交事務。

3. Map

假如我們的實體類屬性過多,用map,傳遞map的key

<insert id="addUser2" parameterType="map">
insert into mybatis.user (id, name, pwd) values (#{id1}, #{name1}, #{pwd1});
</insert>
int addUser2(Map<String, Object> map);
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id1",5);
map.put("name1","dong");
map.put("pwd1","12345");
mapper.addUser2(map);

//提交事務
sqlSession.commit();
sqlSession.close();
}

4.模糊查詢

java程式碼執行的時候,傳遞萬用字元%

<select id="getUserLike" resultType="com.hou.pogo.User">
select * from mybatis.user where name like #{value}
</select>
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getUserLike("%o%");

for(User user : list){
System.out.println(user);
}

sqlSession.close();
}

3. 配置解析

1. 核心配置檔案

  • mybatis-config.xml

configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(資料來源)
databaseIdProvider(資料庫廠商標識)
mappers(對映器)

2. 環境配置(environments)

MyBatis 可以配置成適應多種環境

不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境。

Mybatis 預設的事務管理器是JDBC,連線池:POOLED

3. 屬性

我們可以通過properties屬性來引用配置檔案

這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。 (db.properties)

編寫一個配置檔案

db.properties

driver = com.mysql.jdbc.Driver
url = "jdbc:mysql://111.230.212.103:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"
username = root
password = hdk123

在核心配置檔案中引入

mybatis-config.xml (同時有的話,優先走外面properties)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<!--引入外部配置檔案-->
<!--<properties resource="db.properties"/>-->

<properties resource="db.properties">
<property name="username" value="root"></property>
<property name="password" value="hdk123"></property>
</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>

<!--每一個mapper.xml都需要註冊-->
<mappers>
<mapper resource="com/hou/dao/UserMapper.xml"/>
</mappers>

</configuration>

4. 類型別名(typeAliases)

類型別名可為 Java 型別設定一個縮寫名字。

<typeAliases>
<typeAlias type="com.hou.pogo.User" alias="User"></typeAlias>
</typeAliases>

掃描實體類的包,預設別名就為這個類的類名首字母小寫

<typeAliases>
<package name="com.hou.pogo"></package>
</typeAliases>

在實體類,比較少的時候使用第一種,實體類多使用第二種。

第一種可以自定義,第二則不行,但是 若有註解,則別名為其註解值 。

@Alias("hello")
public class User {
}

5. 設定

設定名描述有效值預設值
cacheEnabled 全域性性地開啟或關閉所有對映器配置檔案中已配置的任何快取。 true | false true
lazyLoadingEnabled 延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入。 特定關聯關係中可通過設定 fetchType 屬性來覆蓋該項的開關狀態。 true | false false
logImpl 指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未設定

6. 其他

7. 對映器

方式一: [推薦使用]

<mappers>
<mapper resource="com/hou/dao/UserMapper.xml"/>
</mappers>

方式二:

<mappers>
<mapper class="com.hou.dao.UserMapper" />
</mappers>
  • 介面和它的Mapper必須同名

  • 介面和他的Mapper必須在同一包下

方式三:

<mappers>
<!--<mapper resource="com/hou/dao/UserMapper.xml"/>-->
<!--<mapper class="com.hou.dao.UserMapper" />-->
<package name="com.hou.dao" />
</mappers>
  • 介面和它的Mapper必須同名

  • 介面和他的Mapper必須在同一包下

8.生命週期和作用域

作用域和生命週期類別是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題

SqlSessionFactoryBuilder:

  • 一旦建立了 SqlSessionFactory,就不再需要它了 。

  • 區域性變數

SqlSessionFactory

  • 就是資料庫連線池。

  • 一旦被建立就應該在應用的執行期間一直存在 ,沒有任何理由丟棄它或重新建立另一個例項 。 多次重建 SqlSessionFactory 被視為一種程式碼“壞習慣”。

  • 因此 SqlSessionFactory 的最佳作用域是應用作用域。

  • 最簡單的就是使用單例模式或者靜態單例模式。

SqlSession

  • 每個執行緒都應該有它自己的 SqlSession 例項。

  • 連線到連線池的請求!

  • SqlSession 的例項不是執行緒安全的,因此是不能被共享的 ,所以它的最佳的作用域是請求或方法作用域。

  • 用完之後趕緊關閉,否則資源被佔用。

4. 解決屬性名和欄位名不一致的問題

資料庫中的欄位

新建一個專案,拷貝之前,測試實體欄位不一致的情況

User

package com.hou.pogo;

public class User {

private int id;
private String name;
private String password;
}

問題:

User{id=2, name='wang', password='null'}

解決方法:

核心配置檔案

  • 起別名

<select id="getUserById" resultType="User"
parameterType="int">
select id,name,pwd as password from mybatis.user where id = #{id}
</select>
  • resultMap 結果集對映

<?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繫結一個對應的mapper介面-->
<mapper namespace="com.hou.dao.UserMapper">

<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from mybatis.user where id = #{id}
</select>

<!--結果集對映-->
<resultMap id="UserMap" type="User">
<!--colunm 資料庫中的欄位,property實體中的屬性-->
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="pwd" property="password"></result>
</resultMap>

</mapper>
  • resultMap 元素是 MyBatis 中最重要最強大的元素。

  • ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。

<resultMap id="UserMap" type="User">
<!--colunm 資料庫中的欄位,property實體中的屬性-->
<!--<result column="id" property="id"></result>-->
<!--<result column="name" property="name"></result>-->
<result column="pwd" property="password"></result>
</resultMap>

5. 日誌

1. 日誌工廠

如果一個數據庫操作出現了異常,我們需要排錯。日誌就是最好的助手。

曾經:sout,debug

現在:日誌工廠

logImpl

  • SLF4J

  • LOG4J [掌握]

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING [掌握]

  • NO_LOGGING

具體使用哪一個,在設定中設定

STDOUT_LOGGING 標誌日誌輸出

mybatis-confi中

<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

2. Log4j

  1. 先導包

    pom.xml下

    <dependencies>
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    </dependencies>
  2. 新建log4j.properties檔案

### set log levels ###
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/hou.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
  1. 配置實現

<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
  1. Log4j使用

package com.hou.dao;

import com.hou.pojo.User;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

public class UserDaoTest {

static Logger logger = Logger.getLogger(UserDaoTest.class);

@Test
public void test(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
logger.info("測試");
User user = userDao.getUserById(2);
System.out.println(user);
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void testLog4j(){
logger.info("info:進入了testlog4j");
logger.debug("debug:進入了testlog4j");
logger.error("error:進入了testlog4j");
}

}

6. 分頁

1. Limit 分頁

語法:

SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 0,2;
package com.hou.dao;

import com.hou.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {

//根據id查詢使用者
User getUserById(int id);

List<User> getUserByLimit(Map<String, Integer> map);

}

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繫結一個對應的mapper介面-->
<mapper namespace="com.hou.dao.UserMapper">

<select id="getUserById" resultMap="UserMap"
parameterType="int">
select * from mybatis.user where id = #{id}
</select>

<!--結果集對映-->
<resultMap id="UserMap" type="User">
<!--colunm 資料庫中的欄位,property實體中的屬性-->
<!--<result column="id" property="id"></result>-->
<!--<result column="name" property="name"></result>-->
<result column="pwd" property="password"></result>
</resultMap>

<select id="getUserByLimit" parameterType="map"
resultType="User" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

</mapper>

test類

@Test
public void getByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex", 1);
map.put("pageSize", 2);
List<User> userList = mapper.getUserByLimit(map);

for(User user:userList){
System.out.println(user);
}

sqlSession.close();
}

2. RowBounds分頁

@Test

@Test
public void getUserByRow(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds實現
RowBounds rowBounds = new RowBounds(1, 2);

//通過java程式碼層面
List<User> userList = sqlSession.selectList
("com.hou.dao.UserMapper.getUserByRowBounds",
null,rowBounds);

for (User user : userList) {
System.out.println(user);
}

sqlSession.close();
}

3. 分頁外掛

  • pageHelper

7. 使用註解開發

  1. 刪除 UserMapper.xml

  2. UserMapper

    package com.hou.dao;

    import com.hou.pojo.User;
    import org.apache.ibatis.annotations.Select;

    import java.util.List;

    public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();
    }
  3. 核心配置 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>

    <!--引入外部配置檔案-->
    <properties resource="db.properties"/>

    <!--可以給實體類起別名-->
    <typeAliases>
    <typeAlias type="com.hou.pojo.User" alias="User"></typeAlias>
    </typeAliases>

    <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 class="com.hou.dao.UserMapper"></mapper>
    </mappers>
    </configuration>

    本質:反射機制

    底層:動態代理!

Mybatis詳細執行流程:

  1. Resource獲取全域性配置檔案

  2. 例項化SqlsessionFactoryBuilder

  3. 解析配置檔案流XMLCondigBuilder

  4. Configration所有的配置資訊

  5. SqlSessionFactory例項化

  6. trasactional事務管理

  7. 建立executor執行器

  8. 建立SqlSession

  9. 實現CRUD

  10. 檢視是否執行成功

  11. 提交事務

  12. 關閉

註解CRUD

package com.hou.dao;

import com.hou.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

@Select("select * from user")
List<User> getUsers();

//方法存在多個引數,所有的引數必須加@Param
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);

@Insert("insert into user (id, name, pwd) values" +
"(#{id},#{name},#{password})")
int addUser(User user);

@Update("update user set name=#{name}, pwd=#{password} " +
"where id=#{id}")
int updateUser(User user);

@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int id);

}

MybatisUtile

package com.hou.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;

//sqlSessionFactory --> sqlSession
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();
}
}

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}

}

Test

package com.hou.dao;

import com.hou.pojo.User;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {

@Test
public void test(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUsers();
for (User user : userList) {
System.out.println(user);
}

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void getuserById(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
User user = userDao.getUserById(1);

System.out.println(user);


}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void addUser(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.addUser(new User(6, "kun","123"));

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void updateUser(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.updateUser(new User(6, "fang","123"));

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void deleteUser(){
// 獲得sqlsession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.deleteUser(6);

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}
}

8. Lombok

  1. 在IDEA中安裝lombok外掛

  2. 配置

    <dependencies>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    </dependency>
    </dependencies>
  3. @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows

    @Data: 無參構造,get,set,toString,hashCode

    在實體類上加註解

    package com.hou.pojo;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {

    private int id;
    private String name;
    private String password;

    }

9. 多對一處理

  • 多個學生關聯一個老師(多對一)

  • 集合(一對多)

1. 建表

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, 'hou');

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, 'xiao1', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (2, 'xiao2', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (3, 'xiao3', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (4, 'xiao4', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (5, 'xiao5', 1);
  1. 新建實體類

    package com.hou.pojo;

    import lombok.Data;

    @Data
    public class Student {
    private int id;
    private String name;

    //學生需要關聯一個老師
    private Teacher teacher;
    }
    package com.hou.pojo;

    import lombok.Data;

    @Data
    public class Teacher {
    private int id;
    private String name;
    }
  2. 建立Mapper介面

  3. 建立Mapper.xml

  4. 測試是否能夠成功

2. 按照查詢巢狀處理

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">

<mapper namespace="com.hou.dao.StudentMapper">

<select id="getStudent" resultMap="StudentTeacher">
select * from student;
</select>

<resultMap id="StudentTeacher" type="com.hou.pojo.Student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<!--物件使用assiociation-->
<!--集合用collection-->
<association property="teacher" column="tid"
javaType="com.hou.pojo.Teacher"
select="getTeacher"></association>
</resultMap>

<select id="getTeacher" resultType="com.hou.pojo.Teacher">
select * from teacher where id = #{id};
</select>

</mapper>

3. 按照結果巢狀處理

select s.id sid,s.name sname,t.name tname
from student s,teacher t where s.tid=t.id;
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname
from student s,teacher t where s.tid=t.id;
</select>

<resultMap id="StudentTeacher2" type="com.hou.pojo.Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<association property="teacher" javaType="com.hou.pojo.Teacher">
<result property="name" column="tname"></result>
</association>

</resultMap>

property 對映到列結果的欄位或屬性。

column 資料庫中的列名,或者是列的別名。

10. 一對多

一個老師擁有多個學生

對於老師而言就是一對多

1.環境搭建

實體類

package com.hou.pojo;

import lombok.Data;
import java.util.List;

@Data
public class Teacher {
private int id;
private String name;
private List<Student> studentList;
}
package com.hou.pojo;

import lombok.Data;

@Data
public class Student {
private int id;
private String name;
private int tid;
}

2. 按照結果查詢

<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname, t.name tname, t.id tid
from student s, teacher t
where s.tid = t.id and t.id = #{id};
</select>

<resultMap id="TeacherStudent" type="com.hou.pojo.Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<!--集合中的泛型資訊,我們用oftype獲取-->
<collection property="studentList" ofType="com.hou.pojo.Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
</collection>
</resultMap>

3. 按照查詢巢狀處理

<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id = #{id}
</select>

<resultMap id="TeacherStudent2" type="com.hou.pojo.Teacher">
<collection property="studentList" column="id" javaType="ArrayList"
ofType="com.hou.pojo.Student"
select="getStudentByTeacherId"></collection>
</resultMap>

<select id="getStudentByTeacherId" resultType="com.hou.pojo.Student">
select * from mybatis.student where tid = #{id}
</select>

小結

  1. 關聯 - association 多對一

  2. 集合 - collection 一對多

  3. javaType & ofType

    1. JavaType用來指定實體中屬性型別

    2. ofType對映到list中的型別,泛型中的約束型別

注意點:

  • 保證sql可讀性,儘量保證通俗易懂

  • 注意欄位問題

  • 如果問題不好排查錯誤,使用日誌

11. 動態sql

動態sql:根據不同的條件生成不同的SQL語句

1. 搭建環境

create table `blog`(
`id` varchar(50) not null comment '部落格id',
`title` varchar(100) not null comment '部落格標題',
`author` varchar(30) not null comment '部落格作者',
`create_time` datetime not null comment '建立時間',
`views` int(30) not null comment '瀏覽量'
)ENGINE=InnoDB DEFAULT CHARSET=utf8

實體類

package com.hou.pojo;

import lombok.Data;

import java.util.Date;

@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}

核心配置

<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

Mapper.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.hou.mapper.BlogMapper">
<insert id="addBlog" parameterType="Blog">
insert into mybatis.blog (id, title, author, create_time, views) values
(#{id}, #{title}, #{author}, #{create_time}, #{views});
</insert>
</mapper>

新建隨機生成ID包

package com.hou.utils;

import org.junit.Test;

import java.util.UUID;

@SuppressWarnings("all")
public class IDUtiles {

public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}

@Test
public void test(){
System.out.println(getId());
}

}

測試類:新增資料

import com.hou.mapper.BlogMapper;
import com.hou.pojo.Blog;
import com.hou.utils.IDUtiles;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;

public class MyTest {

@Test
public void addBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

Blog blog = new Blog();
blog.setId(IDUtiles.getId());
blog.setAuthor("houdongun");
blog.setCreateTime(new Date());
blog.setViews(999);
blog.setTitle("first");

blogMapper.addBlog(blog);

blog.setId(IDUtiles.getId());
blog.setTitle("second");
blogMapper.addBlog(blog);

blog.setId(IDUtiles.getId());
blog.setTitle("third");
blogMapper.addBlog(blog);

blog.setId(IDUtiles.getId());
blog.setTitle("forth");
blogMapper.addBlog(blog);

sqlSession.close();
}
}

2. if

<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != author">
and author = #{author}
</if>
</select>

test

@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();

// map.put("title", "second");
map.put("author", "houdongun");

List<Blog> list = blogMapper.queryBlogIF(map);

for (Blog blog : list) {
System.out.println(blog);
}

sqlSession.close();
}

3. choose、when、otherwise

<select id="queryBlogchoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>

4. trim、where、set

<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>

trim 可以自定義

SQL片段

有些時候我們有一些公共部分

  1. 使用sql便籤抽取公共部分

  2. 在使用的地方使用include標籤

<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>

<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>

注意:

  • 最好基於單表

  • sql裡不要存在where標籤

5. for-each

<!--ids是傳的,#{id}是遍歷的-->
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and ("
close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>

test

@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();

ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(3);
map.put("ids",ids);

List<Blog> list = blogMapper.queryBlogForeach(map);

for (Blog blog : list) {
System.out.println(blog);
}

sqlSession.close();
}

12. 快取(瞭解)

1. 一級快取

  1. 開啟日誌

  2. 測試一個session中查詢兩次相同記錄。

快取失效:

  • 對映語句檔案中的所有 insert、update 和 delete 語句會重新整理快取。

  • 查詢不同的mapper.xml

  • 手動清除快取

一級快取預設開啟,只在一次sqlseesion中有效

2. 二級快取

  1. 開啟全域性快取

<setting name="cacheEnabled" value="true"/>
  1. 在當前mapper.xml中使用二級快取

<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

test

@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserByid(1);
System.out.println(user);
sqlSession.close();

UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.queryUserByid(1);
System.out.println(user1);
System.out.println(user==user1);
sqlSession1.close();
}

只用cache時加序列化

<cache/>

實體類

package com.hou.pojo;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
private int id;
private String name;
private String pwd;

public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
}

小結:

  • 只有開啟了二級快取,在Mapper下有效

  • 所有資料都會先放在一級快取

  • 只有當回話提交,或者關閉的時候,才會提交到二級快取

3. 自定義快取-ehcache

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>

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">
<!--
diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
user.home – 使用者主目錄
user.dir – 使用者當前工作目錄
java.io.tmpdir – 預設臨時檔案路徑
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--
defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
-->
<!--
name:快取名稱。
maxElementsInMemory:快取最大數目
maxElementsOnDisk:硬碟最大快取個數。
eternal:物件是否永久有效,一但設定了,timeout將不起作用。
overflowToDisk:是否儲存到磁碟,當系統當機時
timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush:記憶體數量最大時是否清除。
memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
FIFO,first in first out,這個是大家最熟的,先進先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
-->
<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>