一、前言

上次用Mybatis还是2017年做项目的时候,已经很久过去了。中途再没有用过Mybatis。导致现在学习SpringBoot过程中遇到一些Mybatis的问题,以此做出总结(XML极简模式)。当然只是实用方面的总结,具体就不深究♂了。这里只总结怎么用!!!

(这次直接跳到十一,是因为中间是RabbitMQ 详解,大家看微笑哥的就够了)

二、关于Mybatis

1、什么是Mybatis

(1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

(2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

(3)通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。

2、Mybaits的优点

(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;

(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。

(4)能够与Spring很好的集成;

(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

3、MyBatis框架的缺点

(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

4、MyBatis框架适用场合

(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。

(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。

5、MyBatis与Hibernate有哪些不同

(1)Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。

(2)Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。

(3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。


三、使用总结

以下的用法实例建议将源码clone到本地运行,全部使用的是XMl极简模式

因为我没有贴出完整的代码,只贴出关键处理的部分

所有测试都已经通过Postman发送请求测试。

不过我建议各位看官可以用下IDEA的插件:Restfultookit,非常好用的,根据controller定义的url地址快捷生成请求报文,可以直接测试。对于测试报文来说这个插件简直无敌!强烈推荐(已经安装的当我没说)


1、Java,JDBC与MySQL数据类型对照数据类型关系表

任何MySQL数据类型都可以转换为Java数据类型。

如果选择的Java数值数据类型的精度或容量低于要转换为的MySQL数据类型,则可能会出现舍入,溢出或精度损失。

下表列出了始终保证有效的转换。 第一列列出了一种或多种MySQL数据类型,第二列列出了可以转换MySQL类型的一种或多种Java类型。

These MySQL Data Types Can always be converted to these Java types
CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String, java.io.InputStream, java.io.Reader, java.sql.Blob, java.sql.Clob
FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT java.lang.String, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double, java.math.BigDecimal
DATE, TIME, DATETIME, TIMESTAMP java.lang.String, java.sql.Date, java.sql.Timestamp

ResultSet.getObject()方法使用MySQL和Java类型之间的类型转换,遵循适当的JDBC规范。 ResultSetMetaData.GetColumnTypeName()和ResultSetMetaData.GetColumnClassName()返回的值如下表所示。 有关JDBC类型的更多信息,请参阅java.sql.Types类的参考。

MySQL Type Name Return value of GetColumnTypeName Return value of GetColumnClassName
BIT(1) BIT java.lang.Boolean
BIT( > 1) BIT byte[]
TINYINT TINYINT java.lang.Boolean if the configuration property tinyInt1isBit is set to true (the default) and the storage size is 1, or java.lang.Integer if not.
BOOL, BOOLEAN TINYINT See TINYINT, above as these are aliases for TINYINT(1), currently.
SMALLINT[(M)] [UNSIGNED] SMALLINT [UNSIGNED] java.lang.Integer (regardless of whether it is UNSIGNED or not)
MEDIUMINT[(M)] [UNSIGNED] MEDIUMINT [UNSIGNED] java.lang.Integer (regardless of whether it is UNSIGNED or not)
INT,INTEGER[(M)] [UNSIGNED] INTEGER [UNSIGNED] java.lang.Integer, if UNSIGNED java.lang.Long
BIGINT[(M)] [UNSIGNED] BIGINT [UNSIGNED] java.lang.Long, if UNSIGNED java.math.BigInteger
FLOAT[(M,D)] FLOAT java.lang.Float
DOUBLE[(M,B)] DOUBLE java.lang.Double
DECIMAL[(M[,D])] DECIMAL java.math.BigDecimal
DATE DATE java.sql.Date
DATETIME DATETIME java.sql.Timestamp
TIMESTAMP[(M)] TIMESTAMP java.sql.Timestamp
TIME TIME java.sql.Time
YEAR[(2|4)] YEAR If yearIsDateType configuration property is set to false, then the returned object type is java.sql.Short. If set to true (the default), then the returned object is of type java.sql.Date with the date set to January 1st, at midnight.
CHAR(M) CHAR java.lang.String (unless the character set for the column is BINARY, then byte[] is returned.
VARCHAR(M) [BINARY] VARCHAR java.lang.String (unless the character set for the column is BINARY, then byte[] is returned.
BINARY(M) BINARY byte[]
VARBINARY(M) VARBINARY byte[]
TINYBLOB TINYBLOB byte[]
TINYTEXT VARCHAR java.lang.String
BLOB BLOB byte[]
TEXT VARCHAR java.lang.String
MEDIUMBLOB MEDIUMBLOB byte[]
MEDIUMTEXT VARCHAR java.lang.String
LONGBLOB LONGBLOB byte[]
LONGTEXT VARCHAR java.lang.String
ENUM('value1','value2',...) CHAR java.lang.String
SET('value1','value2',...) CHAR java.lang.String

参考:6.5 Java, JDBC, and MySQL Types

2、当实体类中的属性名和表中的字段名不一样,怎么办

其一:定义字段别名,使之与实体类属性名一致。

<!-- 查询用户信息列表1 -->
<select id="queryUserList1" resultType="com.niaobulashi.entity.SysUser">
SELECT
u.user_id, u.username userNameStr, u.password, u.salt, u.email,
u.mobile, u.status, u.dept_id, u.create_time
FROM
sys_user u
where 1=1
</select>

其二:通过resultMap映射字段名和实体类属性名保持一致

<resultMap id="sysUserInfoMap" type="com.niaobulashi.entity.SysUser">
<!-- 用户Id属性来映射主键字段 userId-->
<id property="id" column="userId"/>
<!-- 用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性-->
<result property="userNameStr" column="username"/>
</resultMap> <!--用户Vo-->
<sql id="selectSysUserVo">
SELECT
u.user_id, u.username, u.password, u.salt,
u.email, u.mobile, u.status, u.dept_id, u.create_time
FROM
sys_user u
</sql> <!-- 查询用户信息列表2 -->
<select id="queryUserList2" resultMap="sysUserInfoMap">
<include refid="selectSysUserVo"/>
where 1=1
</select>

推荐使用第二种。

2、获取Mybatis自增长主键

思路:useGeneratedKeys="true" keyProperty="id"

<!-- 获取自动生成的(主)键值 -->
<insert id="insertSysTest" parameterType="com.niaobulashi.model.SysTest"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO sys_test(name, age, nick_name) VALUES (#{name},#{age},#{nickName})
</insert>

获取自增长主键

/**
* 获取自增长主键ID
* @param sysTest
* @throws Exception
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
private void addSysTest(@RequestBody SysTest sysTest) throws Exception {
try {
SysTest sysTestParam = new SysTest();
// 将传入参数Copy到新申明的对象中,这样才能从sysTestParam中获取到自增长主键
BeanUtils.copyProperties(sysTest, sysTestParam);
this.sysTestService.insertSysTest(sysTestParam);
log.info("获取自增长主键为:" + sysTestParam.getId());
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}

3、模糊查询

使用%"#{value}"%"方法会引起SQL注入

推荐使用:CONCAT('%',#{value},'%')

<!--用户Vo-->
<sql id="selectSysUserVo">
SELECT
u.user_id, u.username, u.password, u.salt,
u.email, u.mobile, u.status, u.dept_id, u.create_time
FROM
sys_user u
</sql> <!-- 查询用户信息列表2 -->
<select id="queryUserListByName" parameterType="String" resultMap="sysUserInfoMap">
<include refid="selectSysUserVo"/>
where 1=1
and u.username like concat('%',#{userName},'%')
</select>

4、多条件查询

1、使用@Param

List<SysUser> queryUserByNameAndEmail(@Param("userName") String userName, @Param("email") String email);
<!--使用用户名和邮箱查询用户信息-->
<select id="queryUserByNameAndEmail" resultMap="sysUserInfoMap">
<include refid="selectSysUserVo"/>
<where>
<if test="userName != null and userName != ''">
AND u.username like concat('%',#{userName},'%')
</if>
<if test="email != null and email != ''">
AND u.email like concat('%',#{email},'%')
</if>
</where>
</select>

2、使用JavaBean

这里给了一些常见的查询条件:日期、金额。

List<SysUser> queryUserByUser(SysUser sysUser);
<select id="queryUserByUser" parameterType="com.niaobulashi.model.SysUser" resultMap="sysUserInfoMap">
<include refid="selectSysUserVo"/>
<where>
1=1
<if test="userNameStr != null and userNameStr != ''">
AND u.username like concat('%', #{userNameStr}, '%')
</if>
<if test="email != null and email != ''">
AND u.email like concat('%', #{email}, '%')
</if>
<if test="mobile != null and mobile != ''">
AND u.mobile like concat('%', #{mobile}, '%')
</if>
<if test="createDateStart != null and createDateStart != ''">/*开始时间检索*/
AND date_format(u.create_time, '%y%m%d') <![CDATA[ >= ]]> date_format(#{createDateStart}, '%y%m%d')
</if>
<if test="createDateEnd != null and createDateEnd != ''">/*结束时间检索*/
AND date_format(u.create_time, '%y%m%d') <![CDATA[ <= ]]> date_format(#{createDateEnd}, '%y%m%d')
</if>
<if test="amtFrom != null and amtFrom != ''">/*起始金额*/
AND u.amt <![CDATA[ >= ]]> #{amtFrom}
</if>
<if test="amtTo != null and amtTo != ''">/*截至金额*/
AND u.amt <![CDATA[ <= ]]> #{amtTo}
</if>
</where>
</select>

5、批量删除foreach

xml部分

<delete id="deleteSysTestByIds" parameterType="String">
delete from sys_test where id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>

其中foreach包含属性讲解:

  • open:整个循环内容开头的字符串。
  • close:整个循环内容结尾的字符串。
  • separator:每次循环的分隔符。
  • item:从迭代对象中取出的每一个值。
  • index:如果参数为集合或者数组,该值为当前索引值,如果参数为Map类型时,该值为Map的key。
  • collection:要迭代循环的属性名。

dao部分

int deleteSysTestByIds(String[] ids);

service层

@Transactional(rollbackFor = Exception.class)
@Override
public int deleteDictDataByIds(String ids) throws Exception{
try {
return sysTestDao.deleteSysTestByIds(ids.split(","));
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}

controller

@RequestMapping(value = "/deleteIds", method = RequestMethod.POST)
public int deleteIds(String ids) throws Exception {
try {
return sysTestService.deleteDictDataByIds(ids);
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}

请求URL:http://localhost:8081/test/deleteIds

请求报文:

ids : 1,2

6、多表查询association和collection

多表查询,多表肯定首先我们先要弄清楚两个关键字:

association: 一对一关联(has one)collection:一对多关联(has many)

的各个属性的含义:

association和collection
property:映射数据库列的字段或属性。
colum:数据库的列名或者列标签别名。
javaTyp:完整java类名或别名。
jdbcType:支持的JDBC类型列表列出的JDBC类型。这个属性只在insert,update或delete的时候针对允许空的列有用。
resultMap:一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap。这是一个替代的方式去调用另一个select语句。

这样说起来可能不好理解,我举个栗子

涉及到这三张表,我粗略的画了一下:

- 用户表 部门表 角色表
表名 sys_user sys_dept sys_role
与用户表关系 - 一对一(一个用户只属于一个部门) 一对多(一个用户可以有多个角色)

于是用户表关联部门表,我们用association

用户表关联角色表,我们用collection

当然了,能用得这么蛋疼关键字的前提条件是,你要查询关联的字段,如果你只是关联不查它,那就不需要用这玩意。。

辣么,我结合这两个多表查询的关键字associationcollection举个栗子。

1、用户表实体类

@Data
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
/** 用户ID */
private Long userId;
/** 用户名 */
private String userNameStr;
/** 密码 */
private String password;
/** 盐 */
private String salt;
/** 邮箱 */
private String email;
/** 手机号 */
private String mobile;
/** 状态 0:禁用 1:正常 */
private Integer status;
/** 部门Id */
private Long deptId;
/** 创建时间 */
private Date createTime;
/****************关联部分**************
/** 部门 */
private SysDept dept;
/** 角色集合 */
private List<SysRole> roles;
}

2、部门表实体类

@Data
public class SysDept implements Serializable {
/** 部门ID */
private Long deptId;
/** 部门名称 */
private String deptName;
}

3、角色表实体类

@Data
public class SysRole implements Serializable {
/** 角色ID */
private Long roleId;
/** 角色名称 */
private String roleName;
}

4、Mapper、Service部分(略)

List<SysUser> queryUserRoleDept(SysUser user);

5、XML部分

<!--查看用户部门和角色信息-->
<select id="queryUserRoleDept" parameterType="com.niaobulashi.model.SysUser" resultMap="UserResult">
select u.user_id, u.username, u.dept_id, d.dept_name, r.role_id, r.role_name
from sys_user u
LEFT JOIN sys_dept d on d.dept_id = u.dept_id
LEFT JOIN sys_user_role ur on ur.user_id = u.user_id
LEFT JOIN sys_role r on r.role_id = ur.role_id
WHERE 1=1
<if test="userId != null and userId != ''">
AND u.user_id = #{userId}
</if>
</select>

UserResult部分

<!--用户表-->
<resultMap type="com.niaobulashi.model.SysUser" id="UserResult">
<id property="userId" column="user_id"/>
<result property="userNameStr" column="username"/>
<result property="password" column="login_name"/>
<result property="salt" column="password"/>
<result property="email" column="email"/>
<result property="mobile" column="mobile"/>
<result property="status" column="status"/>
<result property="deptId" column="dept_id"/>
<result property="createTime" column="create_time"/>
<association property="dept" column="dept_id" javaType="com.niaobulashi.model.SysDept" resultMap="DeptResult"/>
<collection property="roles" javaType="java.util.List" resultMap="RoleResult"/>
</resultMap> <!--部门表-->
<resultMap id="DeptResult" type="com.niaobulashi.model.SysDept">
<id property="deptId" column="dept_id"/>
<result property="deptName" column="dept_name"/>
</resultMap> <!--角色表-->
<resultMap id="RoleResult" type="com.niaobulashi.model.SysRole">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
</resultMap>

6、Controller部分

@RequestMapping(value = "/queryUserRoleDept", method = RequestMethod.POST)
private List<SysUser> queryUserRoleDept(@RequestBody SysUser sysUser) {
List<SysUser> userList = sysUserService.queryUserRoleDept(sysUser);
return userList;
}

7、测试部分

请求结果:

7、分页插件

使用分页插件PageHelper Spring Boot Starter,引入maven依赖:PageHelper Spring Boot Starter1.2.12

application.yml配置

# PageHelper分页插件
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql

controller

@RequestMapping(value = "/queryUserByPage", method = RequestMethod.GET)
private PageInfo queryUserByPage(Integer currentPage, Integer pageSize) {
PageHelper.startPage(currentPage, pageSize);
List<SysUser> userList = sysUserService.queryUserRoleDept(new SysUser());
PageInfo info=new PageInfo(userList);
return info;
}

参考:https://www.cnblogs.com/java-gcs/p/10979821.html

目前暂时写到这里,本篇会持续补充

To be continued

Spring Boot2(十一):Mybatis使用总结(自增长、多条件、批量操作、多表查询等等)的更多相关文章

  1. mybatis 一对多的注入 指的是连表查询时候 将不同的查询结果以列表存储对象形式 注入进去 多对一指的是 查询多条结果但都是一样的 只需注入一条

    mybatis 一对多的注入 指的是连表查询时候 将不同的查询结果以列表存储对象形式 注入进去 多对一指的是 查询多条结果但都是一样的 只需注入一条

  2. 在MyBatis中查询数据、涉及多参数的数据访问操作、插入数据时获取数据自增长的id、关联表查询操作、动态SQL、关于配置MyBatis映射没有代码提示的解决方案

    1. 单元测试 在单元测试中,每个测试方法都需要执行相同的前置代码和后置代码,则可以自定义2个方法,分别在这2个方法中执行前置代码和后置代码,并为这2个方法添加@Before和@After注解,然后, ...

  3. MyBatis总结六:resultMap详解(包含多表查询)

    简介: MyBatis的每一个查询映射的返回类型都是ResultMap,只是当我们提供的返回类型属性是resultType的时候,MyBatis对自动的给我们把对应的值赋给resultType所指定对 ...

  4. Spring Boot2(一):使用Spring Boot2集成Mybatis基础搭建

    Mybatis 初期使用比较麻烦,需要各种配置文件.实体类.Dao 层映射关联.还有一大推其它配置.mybatis-spring-boot-starter 就是 Spring Boot+ Mybati ...

  5. Spring Boot2(二):使用Spring Boot2集成Mybatis缓存机制

    前言 学习SpringBoot集成Mybatis的第二章,了解到Mybatis自带的缓存机制,在部署的时候踩过了一些坑.在此记录和分享一下Mybatis的缓存作用. 本文章的源码再文章末尾 什么是查询 ...

  6. 快速搭建spring boot2.0 项目

    快速搭建spring boot2.0+mybatis+thymeleaf 项目 使用工具STS 3.9.7(eclipse) 首先创建一个spring boot2.0项目(具体创建方法就不写了) 然后 ...

  7. mysql表查询、多表查询(增强查询的使用)子查询、合并查询,外连接,mysql5种约束,自增长

    一.查询加强 1.在mysql中,日期类型可以直接比较,需要注意格式 2.%:表示0到多个字符, _:表示单个字符 exp:显示第二个字符为大写O的所有员工的姓名和工资 select  name fr ...

  8. Spring Boot2 系列教程(二十一)整合 MyBatis

    前面两篇文章和读者聊了 Spring Boot 中最简单的数据持久化方案 JdbcTemplate,JdbcTemplate 虽然简单,但是用的并不多,因为它没有 MyBatis 方便,在 Sprin ...

  9. 构建第一个Spring Boot2.0应用之集成mybatis(六)

    一.环境: IDE:IntelliJ IDEA 2017.1.1 JDK:1.8.0_161 Maven:3.3.9 springboot:2.0.2.RELEASE 二.步骤 方式一:利用配置文件配 ...

随机推荐

  1. 关于JSP

    JSP的本质 JSP本质上就是Servlet, 正常情况下, 它会在第一次被访问的时候被容器转化成Java代码, 然后再从Java代码编译成.class文件, 之后实际就和Servlet没区别了, 也 ...

  2. Qt设置窗口的初始大小(使用sizeHint这个虚函数,或者在构造函数里使用resize函数)

    我们用qt创建一个窗口,先后显示它,代码如下: class Mywindow : public QMainWindow{ ..... } int main( int argc, char** argv ...

  3. C# WindowsPrincipal(Windows规则)的使用

    using System;using System.Collections.Generic;using System.Linq;using System.Security.Principal;usin ...

  4. 图像Stride求取

    原文:图像Stride求取 做这个日志也许你会觉得多余,但是,如果只给你了图像的流文件,和图像的Width,让你还原原始图像,那么你会发现一个问题,就是Stride未知的问题,这时就需要根据图像的Wi ...

  5. tensorflow 1.0 学习:模型的保存与恢复

    将训练好的模型参数保存起来,以便以后进行验证或测试,这是我们经常要做的事情.tf里面提供模型保存的是tf.train.Saver()模块. 模型保存,先要创建一个Saver对象:如 saver=tf. ...

  6. WPF Textblock Run 空白问题

    消除Run之前的空白是将Run标签布局时头尾相连 如: <TextBlock > <Run Text="A"></Run> <Run Te ...

  7. WebApi 中FromUri参数自动解析成实体的要求

    条件一:类属性名称必须和参数名称相同(不分大小写) 条件二:API参数必须以[FromUri]来修饰(数组也需要添加,否则参数传递不了) 条件三:属性类型为“类”的,如果使用类名(导航属性在本类的名称 ...

  8. 开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石

    微信于2013年开源的ibco库,是微信后台大规模使用的c/c++协程库,2013年至今稳定运行在微信后台的数万台机器上.libco在2013年的时候作为腾讯六大开源项目首次开源,ibco支持后台敏捷 ...

  9. QSettings 用法总结(很好用)

    原文  http://hi.baidu.com/fightiger/blog/item/ba69a434f36e18add1a2d350.html 用户对应用程序经常有这样的要求:要求它能记住它的se ...

  10. 什么是Android NDK

    1.NDK是一系列工具的集合. NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk.这些工具对开发者的帮助是巨大的. NDK集成了交叉编译 ...