Mybatis系列目標:從入門開始開始掌握一個高級開發所需要的Mybatis技能。
這是mybatis系列第2篇。
本篇技術棧
-
mysql5.7.25
-
maven3.6.1
-
jdk1.8
-
idea
本篇主要內容
-
通過一個案例感受一下mybatis的強大之處
-
mybatis開發項目的具體步驟
-
介紹mybatis中主要的幾個對象
我們先來一個案例,讓大家感受一下mybatis是多麼的牛逼,我相信大家看了案例之後,會強烈的激發大家學習mybatis的興趣。
案例:原來ibatis是這麼強大
下面的案例,大家先不用關係代碼爲什麼這麼寫,先感受一下效果,後面我們再來細說代碼,案例代碼文章尾部有獲取方式。
準備數據庫
mysql中運行下面腳本:
/*創建數據庫javacode2018*/
DROP DATABASE IF EXISTS `javacode2018`;
CREATE DATABASE `javacode2018`;
USE `javacode2018`;
/*創建表結構*/
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE t_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主鍵,用戶id,自動增長',
`name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '姓名',
`age` SMALLINT NOT NULL DEFAULT 1 COMMENT '年齡',
`salary` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '薪水'
) COMMENT '用戶表';
SELECT * FROM t_user;
上面腳本中,創建了一個
javacode2018
數據庫,然後創建了一個用戶表,裏面有4個字段,id爲主鍵自動增長。
我們的需求
對t_user表,我們有以下這些需求:
-
實現一個通用的插入操作:支持動態插入,可以根據傳入的字段的值,動態生成所需要的各種insert語句
-
批量插入功能
-
實現一個通用的更新操作:支持動態更新操作,可以根據傳入的字段,動態生成所需要的各種update語句
-
實現一個通用的查詢操作:支持各種組合條件查詢、支撐排序、分頁、支持返回列的控制等各種複雜的查詢需求
下面我們就來一個案例,將上面這些需求通過mybatis實現,先見證一下mybatis的強大之處。
創建maven項目
idea中創建maven項目,這個操作在maven系列中已經說過很多次了,以後大部分項目都是通過maven來玩的,maven這方面玩的不是太溜的,可以跳到文章尾部去看一下maven系列的文章。
項目採用maven中聚合及繼承的方式來管理。
創建父項目
先創建父項目mybatis-series
,父項目的座標信息:
<groupId>com.javacode2018</groupId>
<artifactId>mybatis-series</artifactId>
<version>1.0-SNAPSHOT</version>
創建子項目
創建一個子模塊chat01
,子模塊的座標信息:
<groupId>com.javacode2018</groupId>
<artifactId>chat01</artifactId>
<version>1.0-SNAPSHOT</version>
項目結構
如下圖:
引入mybatis依賴
mybatis-series/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>com.javacode2018</groupId>
<artifactId>mybatis-series</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>chat01</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 配置maven編譯的時候採用的編譯器版本 -->
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<!-- 指定源代碼是什麼版本的,如果源碼和這個版本不符將報錯,maven中執行編譯的時候會用到這個配置,默認是1.5,這個相當於javac命令後面的-source參數 -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 該命令用於指定生成的class文件將保證和哪個版本的虛擬機進行兼容,maven中執行編譯的時候會用到這個配置,默認是1.5,這個相當於javac命令後面的-target參數 -->
<maven.compiler.target>1.8</maven.compiler.target>
<mybatis.version>3.5.3</mybatis.version>
<mysql.version>5.1.47</mysql.version>
<lombok.version>1.18.10</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
chat01/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">
<parent>
<artifactId>mybatis-series</artifactId>
<groupId>com.javacode2018</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>chat01</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>
上面我們引入了mybatis需要的包、mysql jdbc驅動、lombok、單元測試需要的junit包、日誌輸出需要的logback包。
這裏lombok可能大家沒有用過,這個東西可以自動幫我們生成javabean的一些代碼,比如get、set方法,可以節省開發編寫代碼的量,這個以後有空了寫一篇文章來介紹。
配置logback
mybatis在運行過程中會輸出一些日誌,比如sql信息、sql的參數信息、執行的結果等信息,mybatis中會通過logback輸出出來。
在chat01/src/main/resources
目錄中新建文件logback.xml
,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.javacode2018" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>
創建mybatis相關文件
user.xml
chat01/src/main/resources
目錄中新建user.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">
<mapper namespace="com.javacode2018.mybatis.chat01.UserMapper">
<!-- 插入 -->
<insert id="insert" parameterType="com.javacode2018.mybatis.chat01.UserModel" keyProperty="id" useGeneratedKeys="true">
<![CDATA[ INSERT INTO `t_user` ]]>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id!=null">
<![CDATA[ `id`, ]]>
</if>
<if test="name!=null">
<![CDATA[ `name`, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age`, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary`, ]]>
</if>
</trim>
<![CDATA[ VALUES ]]>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id!=null">
<![CDATA[ #{id}, ]]>
</if>
<if test="name!=null">
<![CDATA[ #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ #{salary}, ]]>
</if>
</trim>
</insert>
<!-- 批量插入 -->
<insert id="insertBatch" parameterType="map">
<![CDATA[ INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES ]]>
<foreach collection="list" separator="," item="item">
(#{item.id}, #{item.name}, #{item.age}, #{item.salary})
</foreach>
</insert>
<!-- 更新 -->
<update id="update" parameterType="com.javacode2018.mybatis.chat01.UserModel">
<![CDATA[ UPDATE `t_user` ]]>
<set>
<if test="name!=null">
<![CDATA[ `name` = #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age` = #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary` = #{salary}, ]]>
</if>
</set>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</update>
<!-- 更新 -->
<update id="updateByMap" parameterType="map">
<![CDATA[ UPDATE `t_user` ]]>
<set>
<if test="name!=null">
<![CDATA[ `name` = #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age` = #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary` = #{salary}, ]]>
</if>
</set>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</update>
<!-- 刪除 -->
<delete id="delete" parameterType="map">
<![CDATA[
DELETE FROM `t_user`
]]>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</delete>
<!-- 查詢記錄 -->
<select id="getModelList" parameterType="map" resultType="com.javacode2018.mybatis.chat01.UserModel">
<![CDATA[
SELECT
]]>
<choose>
<when test="tableColumnList!=null and tableColumnList.size() >= 1">
<foreach collection="tableColumnList" item="item" separator=",">
<![CDATA[ ${item} ]]>
</foreach>
</when>
<otherwise>
<![CDATA[
`id`,
`name`,
`age`,
`salary`
]]>
</otherwise>
</choose>
<![CDATA[
FROM
`t_user` a
]]>
<where>
<if test="id!=null and id.toString()!=''">
<![CDATA[ AND a.`id` = #{id} ]]>
</if>
<if test="idList!=null and idList.size() >= 1">
<![CDATA[ AND a.`id` IN ]]>
<foreach collection="idList" item="item" open="(" separator="," close=")">
<![CDATA[ #{item} ]]>
</foreach>
</if>
<if test="name!=null and name.toString()!=''">
<![CDATA[ AND a.`name` = #{name} ]]>
</if>
<if test="age!=null and age.toString()!=''">
<![CDATA[ AND a.`age` = #{age} ]]>
</if>
<if test="salary!=null and salary.toString()!=''">
<![CDATA[ AND a.`salary` = #{salary} ]]>
</if>
<if test="nameLike!=null and nameLike.toString()!=''">
<![CDATA[ AND a.`name` like '%${nameLike}%' ]]>
</if>
<if test="salaryGte!=null and salaryGte.toString()!=''">
<![CDATA[ AND a.`salary` >= #{salaryGte} ]]>
</if>
</where>
<if test="sort!=null and sort.toString()!=''">
<![CDATA[ order by ${sort} ]]>
</if>
<if test="skip!=null and pageSize!=null">
<![CDATA[ LIMIT #{skip},#{pageSize} ]]>
</if>
</select>
</mapper>
mybatis-config.xml
chat01/src/main/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>
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root123"/>
</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/user.xml"/>
</mappers>
</configuration>
UserMapper接口
package com.javacode2018.mybatis.chat01;
import java.util.List;
import java.util.Map;
/**
* 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
*/
public interface UserMapper {
/**
* 插入用戶信息
*
* @param userModel
* @return
*/
void insert(UserModel userModel);
/**
* 批量插入用戶信息
*
* @param userModelList
*/
void insertBatch(List<UserModel> userModelList);
/**
* 更新用戶信息
*
* @param userModel
* @return
*/
int update(UserModel userModel);
/**
* 通過map來更新用戶記錄
*
* @param map
* @return
*/
int updateByMap(Map<String, Object> map);
/**
* 通過map來刪除用戶記錄
*
* @param map
* @return
*/
int delete(Map<String, Object> map);
/**
* 查詢用戶列表
*
* @param map
* @return
*/
List<UserModel> getModelList(Map<String, Object> map);
}
UserModel類
package com.javacode2018.mybatis.chat01;
import lombok.*;
/**
* 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
private Long id;
private String name;
private Integer age;
private Double salary;
}
這個類上面的註解都是都是lombok中的,通過這些註解,lombok可以幫助我們自動生成上面4個字段的get方法、set方法、無參構造方法、有參有參構造方法、builder模式構建對象的代碼、重寫toString方法,這些都在代碼編譯爲字節碼之前會寫進去,通過lombok代碼是不是精簡了很多,最後生成的代碼大家可以反編譯一下UserModel.class去看一下,感受一下,此處我們就不貼出來了。
UserUtil類
package com.javacode2018.mybatis.chat01;
import lombok.extern.slf4j.Slf4j;
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;
/**
* 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
*/
@Slf4j
public class UserUtil {
private static SqlSessionFactory sqlSessionFactory = build();
public static SqlSessionFactory build() {
try {
return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
@FunctionalInterface
public interface SessionCall<O> {
O call(SqlSession session) throws Exception;
}
@FunctionalInterface
public interface MapperCall<T, O> {
O call(T mapper) throws Exception;
}
public static <T, O> O callMapper(Class<T> tClass, MapperCall<T, O> mapper) throws Exception {
return call(session -> mapper.call(session.getMapper(tClass)));
}
public static <O> O call(SessionCall<O> sessionCall) throws Exception {
try (SqlSession session = sqlSessionFactory.openSession(true);) {
return sessionCall.call(session);
}
}
}
創建單元測試類UserMapperTest
chat01\src\test\java\com\javacode2018\mybatis\chat01
中創建UserMapperTest
,代碼如下:
package com.javacode2018.mybatis.chat01;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.*;
import java.util.stream.Collectors;
/**
* 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
*/
@Slf4j
public class UserMapperTest {
//動態插入
@Test
public void insert() throws Exception {
UserModel userModel1 = UserModel.builder().name("路人甲Java").build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel1);
return null;
});
log.info("插入結果:{}", this.getModelById(userModel1.getId()));
log.info("---------------------");
UserModel userModel2 = UserModel.builder().name("路人").age(30).salary(50000.00).build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel2);
return null;
});
log.info("插入結果:{}", this.getModelById(userModel2.getId()));
}
//批量插入
@Test
public void insertBatch() throws Exception {
List<UserModel> userModelList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
userModelList.add(UserModel.builder().name("路人甲Java-" + i).age(30 + i).salary(10000.00 * i).build());
userModelList.add(UserModel.builder().name("javacode2018-" + i).age(30 + i).salary(10000.00 * i).build());
}
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insertBatch(userModelList);
return null;
});
List<UserModel> userModelList1 = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null));
log.info("結果:{}", userModelList1);
}
//根據用戶id刪除數據
@Test
public void delete() throws Exception {
Map<String, Object> map = new HashMap<>();
//需要刪除的用戶id
map.put("id", 1);
Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.delete(map));
log.info("刪除行數:{}", count);
}
//動態更新
@Test
public void update() throws Exception {
//將userId=2的name修改爲:路人
Long userId1 = 2L;
Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId1).name("ready").build()));
log.info("更新行數:{}", count);
log.info("---------------------");
//將userId=3的name修改爲:路人,薪水爲:1000.88
Long userId2 = 3L;
count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId2).name("ready").salary(1000.88D).build()));
log.info("更新行數:{}", count);
}
//按用戶id查詢
public UserModel getModelById(Long userId) throws Exception {
//查詢指定id的數據
Map<String, Object> map = new HashMap<>();
map.put("id", userId);
return UserUtil.callMapper(UserMapper.class, mapper -> {
List<UserModel> userModelList = mapper.getModelList(map);
if (userModelList.size() == 1) {
return userModelList.get(0);
}
return null;
});
}
//查詢所有數據
@Test
public void getModelList1() throws Exception {
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null));
log.info("結果:{}", userModelList);
}
//查詢多個用戶id對應的數據
@Test
public void getModelListByIds() throws Exception {
List<Integer> idList = Arrays.asList(2, 3, 4).stream().collect(Collectors.toList());
Map<String, Object> map = new HashMap<>();
map.put("idList", idList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("結果:{}", userModelList);
}
//多條件 & 指定返回的列
@Test
public void getModelList2() throws Exception {
//查詢姓名中包含路人甲java以及薪資大於3萬的用戶id、姓名
Map<String, Object> map = new HashMap<>();
map.put("nameLike", "路人甲java");
map.put("salaryGte", 30000.00D);
//需要返回的列
List<String> tableColumnList = new ArrayList<>();
tableColumnList.add("id");
tableColumnList.add("name");
map.put("tableColumnList", tableColumnList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("結果:{}", userModelList);
}
//條件過濾 & 排序 & 分頁查詢數據 & 只返回用戶id、salary
@Test
public void getPage() throws Exception {
//查詢姓名中包含路人甲java以及薪資大於3萬的用戶id,按照薪資倒敘,每頁5條取第1頁
Map<String, Object> map = new HashMap<>();
map.put("nameLike", "路人甲java");
map.put("salaryGte", 30000.00D);
//加入排序參數
map.put("sort", "salary desc");
//加入分頁參數
int page = 1;
int pageSize = 5;
map.put("skip", (page - 1) * pageSize);
map.put("pageSize", pageSize);
//加入需要返回的列
List<String> tableColumnList = new ArrayList<>();
tableColumnList.add("id");
tableColumnList.add("salary");
map.put("tableColumnList", tableColumnList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("結果:{}", userModelList);
}
}
項目最終結構如下
用例:動態插入
運行UserMapperTest#insert
,輸出如下:
37:58.556 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Preparing: INSERT INTO `t_user` ( `name` ) VALUES ( ? )
37:58.605 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Parameters: 路人甲Java(String)
37:58.613 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - <== Updates: 1
37:58.641 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` = ?
37:58.641 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 1(Long)
37:58.663 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 1
37:58.664 [main] INFO c.j.mybatis.chat01.UserMapperTest - 插入結果:UserModel(id=1, name=路人甲Java, age=1, salary=0.0)
37:58.667 [main] INFO c.j.mybatis.chat01.UserMapperTest - ---------------------
37:58.668 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Preparing: INSERT INTO `t_user` ( `name`, `age`, `salary` ) VALUES ( ?, ?, ? )
37:58.675 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Parameters: 路人(String), 30(Integer), 50000.0(Double)
37:58.679 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - <== Updates: 1
37:58.681 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` = ?
37:58.681 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 2(Long)
37:58.683 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 1
37:58.683 [main] INFO c.j.mybatis.chat01.UserMapperTest - 插入結果:UserModel(id=2, name=路人, age=30, salary=50000.0)
UserMapperTest#insert
這個方法主要有4步操作:
步驟1:插入一條用戶記錄,用戶記錄只有name字段有值
步驟2:去db中查詢步驟1中插入的記錄
步驟3:插入一條用戶記錄,這次插入的記錄所有字段都指定了值
步驟4:去db中查詢步驟3中插入的記錄
重點來了:大家認真看一下UserMapperTest#insert
方法的代碼,兩個插入調用都是mapper.insert
方法,傳入的都是UserModel
對象,唯一不同的是這個對象構建的時候字段的值不一樣,最後再認真看一下上面輸出的sql,產生的2個insert也是不一樣的,這個mapper.insert
方法可以根據UserModel
對象字段是否有值來組裝我們需要的sql,是不是很牛逼,這就是動態插入。
用例:批量插入
運行UserMapperTest#insertBatch
,輸出如下:
38:12.425 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - ==> Preparing: INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?)
38:12.476 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - ==> Parameters: null, 路人甲Java-1(String), 31(Integer), 10000.0(Double), null, javacode2018-1(String), 31(Integer), 10000.0(Double), null, 路人甲Java-2(String), 32(Integer), 20000.0(Double), null, javacode2018-2(String), 32(Integer), 20000.0(Double), null, 路人甲Java-3(String), 33(Integer), 30000.0(Double), null, javacode2018-3(String), 33(Integer), 30000.0(Double), null, 路人甲Java-4(String), 34(Integer), 40000.0(Double), null, javacode2018-4(String), 34(Integer), 40000.0(Double), null, 路人甲Java-5(String), 35(Integer), 50000.0(Double), null, javacode2018-5(String), 35(Integer), 50000.0(Double)
38:12.484 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - <== Updates: 10
38:12.502 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a
38:12.502 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters:
38:12.521 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 12
38:12.521 [main] INFO c.j.mybatis.chat01.UserMapperTest - 結果:[UserModel(id=1, name=路人甲Java, age=1, salary=0.0), UserModel(id=2, name=路人, age=30, salary=50000.0), UserModel(id=3, name=路人甲Java-1, age=31, salary=10000.0), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0), UserModel(id=5, name=路人甲Java-2, age=32, salary=20000.0), UserModel(id=6, name=javacode2018-2, age=32, salary=20000.0), UserModel(id=7, name=路人甲Java-3, age=33, salary=30000.0), UserModel(id=8, name=javacode2018-3, age=33, salary=30000.0), UserModel(id=9, name=路人甲Java-4, age=34, salary=40000.0), UserModel(id=10, name=javacode2018-4, age=34, salary=40000.0), UserModel(id=11, name=路人甲Java-5, age=35, salary=50000.0), UserModel(id=12, name=javacode2018-5, age=35, salary=50000.0)]
這次批量插入了10條用戶記錄,可以看到有這樣的輸出:
40:40.727 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - <== Updates: 10
上面這個表示插入影響的行數,10表示插入了10行。
批量插入之後,又執行了全表查詢,這次插入了10條,加上前面的2個單條插入,表中總計12條記錄。
用例:根據用戶id刪除數據
運行UserMapperTest#delete
,輸出如下:
38:36.498 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - ==> Preparing: DELETE FROM `t_user` WHERE `id` = ?
38:36.551 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - ==> Parameters: 1(Integer)
38:36.560 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - <== Updates: 1
38:36.561 [main] INFO c.j.mybatis.chat01.UserMapperTest - 刪除行數:1
用例:動態更新
運行UserMapperTest#update
,輸出如下:
38:51.289 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Preparing: UPDATE `t_user` SET `name` = ? WHERE `id` = ?
38:51.347 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Parameters: ready(String), 2(Long)
38:51.355 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - <== Updates: 1
38:51.356 [main] INFO c.j.mybatis.chat01.UserMapperTest - 更新行數:1
38:51.358 [main] INFO c.j.mybatis.chat01.UserMapperTest - ---------------------
38:51.359 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Preparing: UPDATE `t_user` SET `name` = ?, `salary` = ? WHERE `id` = ?
38:51.360 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Parameters: ready(String), 1000.88(Double), 3(Long)
38:51.363 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - <== Updates: 1
38:51.364 [main] INFO c.j.mybatis.chat01.UserMapperTest - 更新行數:1
UserMapperTest#update
方法,大家也認真看一下,2個更新,調用都是mapper.update
方法,傳入的都是UserModel
類型的參數,只是2個UserModel
對象的字段值不一樣,最後產生的2個update
語句也是不一樣的,這個update
語句是mybatis動態組裝的,mybatis可以根據UserModel
中字段是否爲NULL,來拼裝sql,這個更新是不是很強大。
用例:動態查詢
查詢所有數據
運行UserMapperTest#getModelList1
,輸出如下:
39:10.552 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a
39:10.611 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters:
39:10.639 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 11
39:10.639 [main] INFO c.j.mybatis.chat01.UserMapperTest - 結果:[UserModel(id=2, name=ready, age=30, salary=50000.0), UserModel(id=3, name=ready, age=31, salary=1000.88), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0), UserModel(id=5, name=路人甲Java-2, age=32, salary=20000.0), UserModel(id=6, name=javacode2018-2, age=32, salary=20000.0), UserModel(id=7, name=路人甲Java-3, age=33, salary=30000.0), UserModel(id=8, name=javacode2018-3, age=33, salary=30000.0), UserModel(id=9, name=路人甲Java-4, age=34, salary=40000.0), UserModel(id=10, name=javacode2018-4, age=34, salary=40000.0), UserModel(id=11, name=路人甲Java-5, age=35, salary=50000.0), UserModel(id=12, name=javacode2018-5, age=35, salary=50000.0)]
可以看到sql是沒有查詢條件的。
查詢多個用戶id對應的數據
運行UserMapperTest#getModelListByIds
,輸出如下:
39:38.000 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` IN ( ? , ? , ? )
39:38.064 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 2(Integer), 3(Integer), 4(Integer)
39:38.096 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3
39:38.097 [main] INFO c.j.mybatis.chat01.UserMapperTest - 結果:[UserModel(id=2, name=ready, age=30, salary=50000.0), UserModel(id=3, name=ready, age=31, salary=1000.88), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0)]
上面這個按照id列表查詢也是比較常用的,比如我們在電商中查詢訂單列表,還需要查詢每個訂單對應的商品,此時可以先查詢訂單列表,然後在通過訂單列表拿到所有的商品id集合,然後通過商品id集合去通過上面的方式檢索商品信息,只需要2次查詢就可以查詢出訂單及商品的信息了。
多條件 & 指定返回的列
運行UserMapperTest#getModelList2
,查詢姓名中包含路人甲java以及薪資大於3萬的用戶id、姓名,輸出如下:
41:12.185 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT id , name FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ?
41:12.275 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 30000.0(Double)
41:12.311 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3
41:12.312 [main] INFO c.j.mybatis.chat01.UserMapperTest - 結果:[UserModel(id=7, name=路人甲Java-3, age=null, salary=null), UserModel(id=9, name=路人甲Java-4, age=null, salary=null), UserModel(id=11, name=路人甲Java-5, age=null, salary=null)]
看一下上面select
語句,select後面只有id,name
2個字段,where後面有多個條件,這種查詢也是比較常用的,有些表可能有幾十個字段,可能我們只需要幾個字段,就可以使用上面這種查詢。
條件過濾 & 排序 & 分頁查詢數據 & 只返回用戶id、salary
運行UserMapperTest#getModelList3
,查詢姓名中包含路人甲java以及薪資大於3萬的用戶id,按照薪資倒敘,每頁5條取第1頁,輸出如下:
44:00.719 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT id , salary FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ? order by salary desc LIMIT ?,?
44:00.775 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 30000.0(Double), 0(Integer), 5(Integer)
44:00.805 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3
44:00.806 [main] INFO c.j.mybatis.chat01.UserMapperTest - 結果:[UserModel(id=11, name=null, age=null, salary=50000.0), UserModel(id=9, name=null, age=null, salary=40000.0), UserModel(id=7, name=null, age=null, salary=30000.0)]
大家主要看一下輸出的sql,如下:
SELECT id , salary FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ? order by salary desc LIMIT ?,?
這個sql會根據查詢條件,自動構建出我們需要的sql,這點上面是最厲害的。
案例總結
上面列舉的一些用例基本上包含了我們對db所需的大部分操作,動態sql處理方面體現的最爲強勁,如果讓我們自己寫,我們需要寫很多判斷,而用mybatis這麼簡單就實現了,我們在java代碼中沒有看到一個判斷拼接語句,而這些sql的判斷拼接都在一個文件中:user.xml
中,這個就是mybatis中核心的文件,我們需要寫的sql及判斷邏輯基本上都在這個xml中,大家可以認真去看一下這個xml文件。
mybatis開發項目的具體步驟
項目中引入mybatis maven配置
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
上面的mybatis.version
版本,大家可以在maven社區中央倉庫中去查找最新的,目前最新的是3.5.3
創建mybatis配置文件
mybatis配置文件爲xml格式,可以放在resource
目錄下面,如上面案例中的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>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root123"/>
</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/user.xml"/>
</mappers>
</configuration>
這個文件主要是對mybatis進行全局配置,比如數據源、事務
的配置,如上面的datasource
元素用來配置數據源,數據源中就需要指定數據庫的一些配置信息;還有其他更多的配置,此處先不做具體說明,後面我們慢慢來,整個系列完成之後,這些配置大家都會懂的。
創建mapper xml文件
如上面案例中的user.xml
,大家在打開看看,我們需要對t_user
表所有操作sql就寫在這個文件中,下一篇文章我們會詳細介紹mapper xml
文件的各種寫法,user.xml
文件是對t_user
表的所有操作一般都會放在這個裏面,mybatis如何使用到這個文件呢,我們需要在上面的mybatis配置文件
中引入這個mapper文件,如案例中在mybatis-config.xml
有下面這樣的內容:
<mappers>
<mapper resource="mapper/user.xml"/>
</mappers>
mappers
元素中可以有多個mapper
文件,我們開發的項目中可能有很多表需要操作,那麼對應會有很多mapper xml
文件,我們都需要在mappers
元素中進行引入,然後mybatis纔會使用到。
創建Mapper接口
開發者如何去調用user.xml
中的各種操作去執行sql呢,這時我們就需要一個Mapper接口了
,Mapper
接口會和mapper xml
建立映射關係,當我們調用Mapper
接口中的方法的時候,會間接的調用到mapper xml
中的各種數據的sql操作,Mapper
接口如何和Mapper xml
文件關聯的呢?
大家去看一下user.xml
文件中有個這樣的一個配置:
<mapper namespace="com.javacode2018.mybatis.chat01.UserMapper">
注意上面的namespace
的值,對應的是UserMapper
這個接口完整的引用,通過這個namespace,UserMapper
接口就可以user.xml
建立了映射關係。
user.xml
中又有很多db操作,這些操作會和UserMapper
接口中的方法建立映射關係,當調用UserMapper
中的方法的時候,間接的會調用到user.xml
中對應的操作。
如user.xml
中有下面一段配置:
<!-- 批量插入 -->
<insert id="insertBatch" parameterType="map">
<![CDATA[ INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES ]]>
<foreach collection="list" separator="," item="item">
(#{item.id}, #{item.name}, #{item.age}, #{item.salary})
</foreach>
</insert>
而UserMapper
中有個insertBatch
方法和上面這個insert
批量插入對應,如下:
/**
* 批量插入用戶信息
*
* @param userModelList
*/
void insertBatch(List<UserModel> userModelList);
所以當我們調用UserMapper
中的insertBatch
方法的時候,會間接調用到user.xml
中的 id="insertBatch"
這個操作。
提示一下:接口和mapper xml映射起來間接調用,是通過java動態代理實現的,後面我們會詳解如何實現的。
下面我們就可以使用mybatis來操作db了。
通過mybatis獲取Mapper接口執行對db的操作
上面我們說了,我們可以通過mapper接口來執行對db的操作,獲取Mapper的主要代碼如下:
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
上面代碼中使用到了mybatis中的核心組件,我們具體來看一下。
Mybatis核心對象介紹
SqlSessionFactoryBuilder
這個是一個構建器,通過名字大家也可以感覺到SqlSessionFactoryBuilder
構建器,是用來構建SqlSessionFactory
對象的,SqlSessionFactoryBuilder可以通過讀取mybatis的配置文件,然後構建一個SqlSessionFactory
對象,一個項目中有很多mapper xml
文件,如果每次操作都去重新解析是非常慢的,那麼怎麼辦?
能不能第一次解析好然後放在內存中,以後直接使用,SqlSessionFactoryBuilder就是搞這個事情的,將mybatis配置文件、mapper xml
文件、mapper xml
文件和Mapper 接口
的映射關係,這些都先給解析好,然後放在java對象中,java對象存在於內存中,內存中訪問會非常快的,那麼我們每次去用的時候就不需要重新去解析xml了,SqlSessionFactoryBuilder解析配置之後,生成的對象就是SqlSessionFactory
,這個是一個重量級的對象,創建他是比較耗時的,所以一般一個db我們會創建一個SqlSessionFactory
對象,然後在系統運行過程中會一直存在,而SqlSessionFactoryBuilder用完了就可以釋放了。
SqlSessionFactory
通過名字可以知道,這個是一個工廠,是用來創建SqlSession
的工廠,SqlSessionFactory
是一個重量級的對象,一般一個db對應一個SqlSessionFactory
對象,系統運行過程中會一直存在。
SqlSessionFactory是一個接口,這個接口有2個實現DefaultSqlSessionFactory
和SqlSessionManager
,一般都是通過SqlSessionFactoryBuilder
來創建SqlSessionFactory
對象。
通過SqlSessionFactoryBuilder
來創建SqlSessionFactory
對象主要有2種方式,一種通過讀取mybatis配置文件的方式,另外一種是硬編碼的方式,這個後面會專門抽一篇文件介紹這塊,springboot中會使用到硬編碼的方式,所以這塊會詳細介紹。
SqlSession
我們通過jdbc操作數據庫需要先獲取一個Connection
連接,然後拿着這個連接去對db進行操作,在mybatis中SqlSession
就類似於jdbc中Connection
連接對象,在mybatis中叫做Sql會話對象
,一般我們一個db操作使用一個SqlSession
對象,所以這個對象一般是方法級別的,方法結束之後,這個對象就銷燬了,這個對象可以調用sqlSessionFactory.openSession
的方法來進行獲取。
我們可以直接通過SqlSession對象來調用mapper xml
中各種db操作,需要指定具體的操作的id,id的格式爲namespace.操作的id
。
Mapper接口
我們可以通過SqlSession直接調用mapper xml
中的db操作,不過更簡單的以及推薦的方式是使用Mapper接口,Mapper接口中的方法和mapper xml文件中的各種db操作
建立了映射關係,是通過Mapper接口完整名稱+方法名稱
和mapper xml
中的namespace+具體操作的id
來進行關聯的,然後我們直接調用Mapper接口
中的方法就可以間接的操作db了,使用想當方便,Mapper接口需要通過SqlSession
獲取,傳入Mapper接口對應的Class
對象,然後會返回這個接口的實例,如:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
總結
本篇文章主要通過一個案例來感受一下mybatis可以幹什麼,以及他的強大之處,還需要大家掌握mybatis開發項目的具體步驟,後面的文章將對mybatis中具體的知識點做詳細介紹,讓大家成爲mybatis高手。
案例代碼獲取方式
私信回覆【案例】獲區免費領取方式