Spring Boot2(十一):Mybatis使用总结(自增长、多条件、批量操作、多表查询等等)
一、前言
上次用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
当然了,能用得这么蛋疼关键字的前提条件是,你要查询关联的字段,如果你只是关联不查它,那就不需要用这玩意。。
辣么,我结合这两个多表查询的关键字association、collection举个栗子。
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