Java Web实战(四)Web后端之MyBatis-基础用法详解

文章目录

    • 1. 使用MyBatis
      • 1-1. JDBC介绍
      • 1-2. 数据库连接池
      • 1-3. Lombook
    • 2. mybatis 基础
      • 2-2. CURD操作
        • 2-2-1. delete 操作
        • 2-2-2. 预编译sql
        • 2-2-3. 插入语句
        • 2-2-4. XML-SQL
        • 2-2-5. insert主键回显
      • 2-3. 查询语句
    • 3. 动态SQL
      • 3-1. `<if>`

  • MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发
  • MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
  • 官网: https://mybatis.org/mybatis-3/zh/index.html

1. 使用MyBatis

创建 Springboot 项目
勾选 MyBatis framework 和 Mysql connector 依赖
准备数据库和数据库表

create table user(id int unsigned primary key auto_increment comment 'ID',name varchar(100) comment '姓名',age tinyint unsigned comment '年龄',gender tinyint unsigned comment '性别, 1:男, 2:女',phone varchar(11) comment '手机号'
) comment '用户表';insert into user(id, name, age, gender, phone) VALUES (null,'白眉鹰王',55,'1','18800000000');
insert into user(id, name, age, gender, phone) VALUES (null,'金毛狮王',45,'1','18800000001');
insert into user(id, name, age, gender, phone) VALUES (null,'青翼蝠王',38,'1','18800000002');
insert into user(id, name, age, gender, phone) VALUES (null,'紫衫龙王',42,'2','18800000003');
insert into user(id, name, age, gender, phone) VALUES (null,'光明左使',37,'1','18800000004');
insert into user(id, name, age, gender, phone) VALUES (null,'光明右使',48,'1','18800000005');

书写数据接收实体

public class User {private Integer id;private String name;private short age;private short gender;private String phone;// getter setter constructor toString
}

配置连接信息

# 数据库连接信息 - 四要素
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:mysql://localhost:3306/db_mybatis
spring.datasource.username=root
spring.datasource.password=root226

编写 Mapper 类, 定义 Select 函数

@Mapper
public interface UserMapper {// 在运行时,会自动生成资接口的实现类对象(代理对象),并且将该对象交给TOC容器营理@Select("select * from user")public List<User> listAll();
}

编写Test类, 测试 MyBatis 是否正常工作

1-1. JDBC介绍

Java DataBase Connectivity, 是使用Java语言操作关系型数据库的一套API. 它只提供一些接口, 也即sun公司官方定义的一套操作所有关系型数据库的规范, 各个数据库厂商(例如MySQL, Oracle, SqlServer)去实现这套接口, 提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

JDBC的所有操作均在Java代码中完成, 第一是注册驱动, 例如MySQL, 二是配置连接属性, 包括url/name/password等, 三是连接数据库, 四是单独配置属性和SQL语句并完成数据操作, 最后释放资源. 如图所示, 有三大缺点:
在这里插入图片描述

一是数据库连接属性与数据处理相互独立, 然而它们均放在了代码中, 在数据库连接属性改变时, 还需要修改代码, 难以维护.
二是数据处理过程过于繁琐.
三是每次要与数据库交互都需要连接和释放, 影响性能.

在 MyBatis 框架下, 有如下处理:
一是数据库连接的四要素被放在配置文件, 是全局属性, 当数据库更换时只需要更改配置, 同时, 数据库连接(con, cursor)交由数据库连接池管理, 可重用, 效率高;
二是定义了数据实体, 通过数据实体的各个字段接收查询返回的数据;
三是实现了 Mapper 注解, 在Mapper接口中定义函数并与SQL操作进行绑定, 另外Mapper接口遵循依赖注入机制, 在IOC容器中会存在一个Mapper接口实现类的对象, 因此在其它地方的Java代码中定义完成后即可使用其中的函数。

1-2. 数据库连接池

数据库连接池是个容器, 负责分配、管理数据库连接(Connection), 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个, 释放空闲时间超过最大空闲时间的连接(也即线程申请了数据库连接但未在使用), 来避免因为没有释放连接而引起的数据库连接遗漏

  • 资源重用
  • 提升系统响应速度
  • 避免数据库连接遗漏

标准接口:DataSource
官方(sun)提供的数据库连接池接口, 第三方组织可以实现此接口以构造适合特性的数据库连接池, 常见的有C3P0, DBCP, Druid, Hikari(Springboot default). 后两个使用居多, 其中Druid连接池是阿里巴巴开源的数据库连接池项目, 功能强大, 性能优秀, 是Java语言最好的数据库连接池之一.
更换连接池只需要更改pom.xml

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>

1-3. Lombook

在定义数据实体时, 除了自己定义属性字段, 其他的getter, setter, constructor, toString等等都还要自己去写, 虽然可以去生成, 但这些重复性的代码显得臃肿.

Lombok 是一个实用的Java类库, 能通过注解的形式自动生成 constructor, getter/setter, equals, hashcode, toString 等方法,并可以自动化生成日志变量,简化java开发、提高效率。

引入依赖:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
注解作用
@Getter/@Setter为所有的属性提供get/set方法
@ToString会给类自动生成易阅读的 toString 方法
@EqualsAndHashCode根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法
@Data提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode)
@NoArgsConstructor为实体类生成无参的构造器方法
@AllArgsConstructor为实体类生成除了static修饰的字段之外带有各参数的构造器方法。

2. mybatis 基础

准备工作:

  1. SQL数据
-- 部门管理
create table dept(id int unsigned primary key auto_increment comment '主键ID',name varchar(10) not null unique comment '部门名称',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '部门表';insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());-- 员工管理
create table emp (id int unsigned primary key auto_increment comment 'ID',username varchar(20) not null unique comment '用户名',password varchar(32) default '123456' comment '密码',name varchar(10) not null comment '姓名',gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',image varchar(300) comment '图像',job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',entrydate date comment '入职时间',dept_id int unsigned comment '部门ID',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '员工表';INSERT INTO emp(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2010-01-01',2,now(),now()),(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());
  1. properties配置
# 1. 数据库四要素
# ...
# 2. MySQL输出日志信息
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 3. 自动驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true
  1. 数据实体
package com.rainbow.pojo;
// some import statements here
// ...@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {private Integer id;private String username;private String password;private String name;private Short gender;private String image;private Short job;private LocalDate entrydate;private Integer deptId;private LocalDateTime createTime;private LocalDateTime updateTime;
}

2-2. CURD操作

2-2-1. delete 操作
```Java
package com.rainbow.Mapper;
// some import statements here
// ...@Mapper
public interface EmpMapper {// #{id}: 预编译sql, 防止SQL注入@Delete("delete from tb_emp where id=#{id}")void delete(Integer id);    // 点击删除按钮时依据主键id删除一条记录// some other database operation// insert, update, select ...
}
```
2-2-2. 预编译sql

上述删除语句会预编译为delete from tb_emp where id=?, 运行时自动填入传入参数值

  • 性能更高
    • 执行一条包含多个步骤, 例如SQL语法解析检查, 优化SQL, 编译SQL, 执行SQL; 预编译可以完成前三个步骤并将结果缓存, 使得在执行多条语句时效率更高(例如批量的delete操作)
  • 更安全(防止SQL注入)
    • SQL注入是通过操作输入的数据来修改事先定义好的SQL语句
    • 例如用户登录页面, 基本逻辑是接收前台返回的account和password然后在数据库查询是否包含该记录并据此决定是否允许登入系统.其SQL语句为: select count(*) from tb_user where account='user_input_acc' and password='user_input_pwd', 通过构造user_input_acc和user_input_pwd可以绕过查询使得查询永远为真:
    • user_input_acc=xxxx
    • user_input_pwd=’ or ‘1’='1
    • 则: select count(*) from tb_user where account='xxxx' and password='' or '1'='1'永远为真
  • 预编译则不会直接进行字符串拼接, 防止了SQL注入.
2-2-3. 插入语句

新增一个员工:

package com.rainbow.Mapper;
// 新增一个员工
@Insert("insert into tb_emp(username, password, name, gender, " +"image, job, entrydate, dept_id, create_time, update_time) " +"VALUES (#{username}, #{password}, #{name}, #{gender}, #{image}, " +"#{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
void insert(Emp emp);

这里利用实体对象作为参数传入, 则#{}中的变量会与emp实体字段进行匹配, 需要把命名改为驼峰命名
测试:

@Test
void testInsert(){Emp emp = new Emp();emp.setUsername("Tom");emp.setName("汤姆");emp.setGender((short) 1);emp.setImage("1.jpg");emp.setJob((short) 2);emp.setEntrydate(LocalDate.of(2010, 9, 18));emp.setDeptId(2);emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());empMapper.insert(emp);
}
2-2-4. XML-SQL

当SQL语句很长很复杂时, 以这样的方式书写CURD操作看起来不舒服, 因此MyBatis使用XML语言将函数和SQL绑定:

1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
2. XML映射文件的namespace属性为Mapper接口全限定名一致。
3. XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。

官方文档:
https://mybatis.net.cn/getting-started.html
XML头部实例:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 以下映射可以自定义 -->
<mapper namespace="org.mybatis.example.BlogMapper"><select id="selectBlog" resultType="Blog">select * from Blog where id = #{id}</select>
</mapper>

将2-2-2的insert语句写入XML并测试:

<mapper namespace="com.rainbow.mapper.EmpMapper"><insert id="insert">insert into tb_emp(username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time)VALUES (#{username}, #{password}, #{name}, #{gender}, #{image}, #{job}, #{entrydate},#{deptId}, #{createTime}, #{updateTime})</insert>
</mapper>

原Java代码:

// 新增一个员工
void insert(Emp emp);

MyBatisX
是一个IDEA插件, 可以为Java代码和对应的MySQL代码两个位置相互关联, 鼠标单击即可跳转, 这样定位代码十分方便。

2-2-5. insert主键回显

考虑新增套餐的场景, 在插入套餐条目后, 还需要维护菜品与套餐的关系表, 因此需要在插入套餐后返回其ID;

@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@Insert("insert into tb_emp(username, password, name, gender, image, job, entrydate,"+" dept_id, create_time, update_time)"+"VALUES (#{username}, #{password}, #{name}, #{gender}, #{image}, #{job}, #{entrydate},"+"#{deptId}, #{createTime}, #{updateTime})")
void insert(Emp emp);

正如代码中所示, @Insert注解前添加一个@Options注解, 指明要返回的字段, 那么该字段可以在插入数据的调用者实体的id显示, 注意Options只和Insert搭配, 如果将Insert的SQL语句转到XML中, 则Options失效, 将不能返回主键值.

2-3. 查询语句

通过 id 查找

// EmpMapper
@Select("select id, username, password, name, gender, image, job, " +"entrydate, dept_id, create_time, update_time " +"from tb_emp where id=#{id}")
Emp getByID(Integer id);
// Test
public void testSelect(){System.out.println(empMapper.getByID(10));
}

可以看到, 结果查询到了, 并且 deptId, createTime 等字段也不为空, 这就说明 properties 文件的驼峰开启有效(仅在Select注解里).

条件查询, 姓名模糊查询/性别/入职时间, 这三个条件都是可选的, 因此使用动态SQL

<select id="select" resultType="com.rainbow.pojo.Emp">select id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time from tb_emp<where><if test="name != null">name like concat('%', #{name}, '%') and </if><if test="gender != null">gender = #{gender} and </if><if test="entrydate != null">entrydate between #{begin} and #{end}</if>order by update_time desc</where>
</select>

有两个需要特别注意的点, 一是concat('%', #{name}, '%'), 利用了concat函数将字符串拼接起来, 但同时保留了预编译的特性, 其次注意between和and关键字的使用, 不要使用<>等符号(在xml中算特殊字符), 有一个解决方法是:
使用<![CDATA[]]>,因为CDATA 部分中的所有内容都会被解析器忽略,所以建议使用<![CDATA[]]>, 例如:
<![CDATA[ and create_time <= #{createTime} ]]>

3. 动态SQL

3-1. <if>

场景1: 在员工查询中, 我们有多个查询条件, 例如姓名、性别、入职时间, 通常会依据其中的零个、一个或者多个条件进行查询, 此时查询SQL语句就不是固定的了

解决方案: XML中使用<if><where>标签

场景2: 更新员工信息, 仅更新其中的几个关键字, 如果是静态的, 则其他未更新的字段被设置为null值, 这里需要使用ifset标签

  1. 原始的静态SQL语句:
<update id="update">update tb_empset username=#{username}, password=#{password}, name=#{name},gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate},dept_id=#{deptId}, update_time=#{updateTime}where id=#{id}
</update>

假如只想修改部分字段, 那么很自然的就只设置部分字段的值, 例如:

Emp emp = new Emp();
emp.setId(22);      // 更新 22 号数据
emp.setUsername("Amel0");
emp.setName("阿迈尔0");
emp.setGender((short) 2);
emp.setUpdateTime(LocalDateTime.now());
empMapper.update(emp);

主要问题在于, 第一是其他字段为null值, 这将会覆盖原有值, 这些"其他字段"可能是具有非空约束的, 这样的插入执行将导致异常
因此, 使用动态SQL语句如下:

<update id="update">update tb_emp<set><if test="username !=null">username=#{username},</if><if test="name !=null">name=#{name},</if><if test="gender !=null">gender=#{gender},</if><if test="image !=null">image=#{image},</if><if test="job !=null">job=#{job},</if><if test="entrydate !=null">entrydate=#{entrydate},</if><if test="deptId !=null">dept_id=#{deptId},</if><if test="updateTime !=null">update_time=#{updateTime}</if></set>where id=#{id}
</update>

注意: 1. 不要写错字段名; 2.注意表字段和实体字段的命名对应关系; 3.不要忘了写逗号 4. 一定一定不要写错了字段名!!!

注意拼接时候可能出现SQL语法错误, 需要仔细检查.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/535592.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32点亮LED灯与蜂鸣器发声

STM32之GPIO GPIO在输出模式时可以控制端口输出高低电平&#xff0c;用以驱动Led蜂鸣器等外设&#xff0c;以及模拟通信协议输出时序等。 输入模式时可以读取端口的高低电平或电压&#xff0c;用于读取按键输入&#xff0c;外接模块电平信号输入&#xff0c;ADC电压采集灯 GP…

前端的数据标记协议

文章目录 数据标记协议是什么数据标记协议的作用常见的数据标记协议Open Graph protocol 开放图谱协议基本元数据协议可选元数据结构化属性 —— 元数据的属性多个相同的元数据标签类型元数据的使用方法全局类型使用自定义类型使用对象类型使用歌曲对象类型视频对象类型文章对象…

C++学习路线

C学习路线思维导图&#xff0c;肝了一个星期终于搞定&#xff0c;这么硬核求个赞不过分吧&#xff1f; 思维导图的内容&#xff0c;也是本文的内容框架&#xff0c;坐稳扶好&#xff0c; C 高速快车要发车了&#xff01; 内容我会持续更新&#xff0c;点赞收藏&#xff0c;…

用户视角的比特币和以太坊外围技术整理

1. 引言 要点&#xff1a; 比特币L2基本强调交易内容的隐蔽性&#xff0c;P2P交易&#xff08;尤其是支付&#xff09;成为主流&#xff0c;给用户带来一定负担&#xff08;闪电网络&#xff09;在以太坊 L2 中&#xff0c;一定程度上减少了交易的隐蔽性&#xff0c;主流是实…

罐头鱼AI批量剪辑短视频系统|视频矩阵获客

智能化管理&#xff0c;轻松批量剪辑短视频&#xff01;AI系统助力您的视频营销提效&#xff01; 随着短视频营销的兴起&#xff0c;我们推出了一款AI批量剪辑短视频系统&#xff0c;QQ:290615413让视频制作更加智能高效。以下是系统的主要功能特点&#xff1a; 首页显示&#…

Android Gradle 开发与应用 (六) : 创建buildSrc插件和使用命令行创建Gradle插件

1. 前言 前文中&#xff0c;我们介绍了在Android中&#xff0c;如何基于Gradle 8.2&#xff0c;创建Gradle插件。这篇文章&#xff0c;我们以buildSrc的方式来创建Gradle插件。此外&#xff0c;还介绍一种用Cmd命令行的方式&#xff0c;来创建独立的Gradle插件的方式。 1.1 本…

技术派整合MyBatis-Plus

Mybatis-Plus大家都熟悉了吧&#xff1f;是一个Mybatis的增强&#xff0c;提供了一些额外功能&#xff0c;比如条件构造器、分页插件、代码生成器等以便我们更专注于业务&#xff0c;而不是SQL语句的编写 官方教程&#xff1a;简介 | MyBatis-Plus 整合MyBatis-Plus 非常简单…

【大数据开发--概念篇】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;书生♡&#xff0c;今天主要和大家分享一下大数据的相关概念&#xff0c;以及我们大数据开发的环境&#xff0c;希望对大家有所帮助。 &#x1f49e;&#x1f49e;路漫漫&#xff0c;希望大家坚持下去&#xff0…

【系统架构设计师】系统工程与信息系统基础 01

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 文章目录 系列文章目录 前言 一、系统工程 ★ 二、信息系统生命周期 ★ 信息系统建设原则 三、信息系统开发方法 ★★ 四、信息系统的分类 ★★★ 1.业务处理系统【TPS】 2.管理信息系统【MIS】 3.决策支持系统…

ELK 安装部署

文章目录 1.日志收集规划2.Elasticsearch部署2.1.Elasticsearch安装2.2.Elasticsearch-head安装2.3.Elasticsearch设置分片数2.4.elasticsearch健康检查 3.Kibana部署4.Logstash部署5.Filebeat部署 开源中间件 # Elastic Stackhttps://iothub.org.cn/docs/middleware/ https:/…

【阿里云系列】-利用yaml文件部署NacosXxl-job到ACK

背景介绍 随着容器化的技术成熟落地&#xff0c;拥抱各种成熟的容器化集群平台是加速我们落地的必然之路&#xff0c;目前国内以阿里云、华为云、腾讯云为平台的供应商为主&#xff0c;国外则以AWS&#xff0c;Azure为主&#xff0c;让我们借助平台已有的优势进行快速落地提高…

VMware安装Ubuntu虚拟机

1. 安装VMware VMware中国官网&#xff1a;VMware - Delivering a Digital Foundation For Businesses VMware Workstation Player&#xff08;官方个人免费版&#xff09;&#xff1a;VMware Workstation Player | VMware VMware Workstation Pro&#xff08;商用收费版&am…