一天快速掌握Mybaits[一]
Posted 程序员springmeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一天快速掌握Mybaits[一]相关的知识,希望对你有一定的参考价值。
一、搭环境
Spring Initializr的搭建
创建完毕后的项目结构
此时application的后缀更名为yml,因为这样,看起来更简洁明了,而作用上,无差别
数据库环境的搭建
新建数据库
执行SQL语句
use `mybatis-demo`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`age`,`address`) values (1,'UZI',19,'上海'),(2,'PDD',25,'上海');
id设置为了主键自动递增
yml配置
server:
port: 8098
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # msyql8 加cj mysql8以下去掉cj
url: jdbc:mysql://localhost:3306/mybatis-demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
username: root # url是表示用于与mysql的进行一个连接 如果是本机 可以用localhost 如果不是要更换成ip
password: 123456 #username表示SQL 账号 password表示密码
mybatis:
mapper-locations: classpath:/Mapper/*.xml #resources 目录下的 Mapper 目录下面的所有xml文件
type-aliases-package: com.yhn.entity #自动配置别名
configuration:
map-underscore-to-camel-case: true #开启驼峰命名
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #配置打印SQL语句
项目结构
二、基本的CRUD
一般web开发需要这几个层面
- Controller 控制层面 负责接收前端传过来的参数
- Service 业务处理层 负责业务处理
- Mapper/Dao 数据层 负责数据调用
结构搭建
Controller
package com.yhn.controller;
import com.yhn.entity.User;
import com.yhn.service.UserService;
import org.springframework.web.bind.annotation.*;
/**
* CRUD
* @Description
* @Author YeHaoNan~
* @Date 2/11/2022 23:37
* @Version 1.0.0
**/
@RestController
@RequestMapping("/user")
public class UserController
@Resource
private UserService service;
Service
public interface UserService
ServiceImpl
@Service
public class UserServiceImpl implements UserService
@Resource
private UserMapper mapper;
Mapper
@Mapper
public interface UserMapper
面试题
@RestController 是哪几个注解的复合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController
@AliasFor(
annotation = Controller.class
)
String value() default "";
/*
最主要的就是
@Controller
@ResponseBody
@Controller :
spring会遍历上面扫描出来的所有bean,过滤出那些添加了注解@Controller的bean,将Controller中所有添加了注解@RequestMapping的方法解析出来封装成RequestMappingInfo存储到RequestMappingHandlerMapping中的mappingRegistry。后续请求到达时,会从mappingRegistry中查找能够处理该请求的方法。
@ResponseBody
加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。 可以加方法上面也可以加在类上面,加在类上面的话,那么就表示所有的方法都会自动添加@RequestBody
*/
@Autowired与@Resource 的区别
1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
查询
@RestController
@RequestMapping("/user")
public class UserController
@Resource
private UserService service;
/**
* 查询所有
* @author YeHaoNan~
* @date 2/11/2022 23:37
* @return List<User>
*/
@GetMapping("/findAll")
public List<User> findAll()
return service.findAll();
/**
* 根据id进行查询
* @author YeHaoNan~
* @date 2/11/2022 23:38
* @param id
* @return User
*/
@GetMapping("/findById")
public User findById(Integer id)
return service.findById(id);
public interface UserService
/**
* 查询所有
* @author YeHaoNan~
* @date 2/11/2022 23:38
* @return List<User>
*/
List<User> findAll();
/**
* 根据id进行查询
* @author YeHaoNan~
* @date 2/11/2022 23:38
* @param id
* @return User
*/
User findById(Integer id);
@Service
public class UserServiceImpl implements UserService
@Resource
private UserMapper mapper;
@Override
public List<User> findAll()
return mapper.findAll();
@Override
public User findById(Integer id)
return mapper.findById(id);
@Mapper
public interface UserMapper
/**
* 查询所有
* @author YeHaoNan~
* @date 2/11/2022 23:38
* @return List<User>
*/
List<User> findAll();
/**
* 根据id进行查询
* @author YeHaoNan~
* @date 2/11/2022 23:38
* @param id
* @return User
*/
// @Param 注解 后面会详细讲解 在当前你可以看做不存在
User findById(@Param("id") Integer id);
mybatis 映射文件 后缀 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.yhn.mapper.UserMapper">
<select id="findAll" resultType="com.yhn.entity.User">
select * from user
</select>
<select id="findById" resultType="com.yhn.entity.User">
select * from user where id = #id
</select>
</mapper>
解析 : Mybatis里面 Mapper中的namespace用于绑定Dao/Mapper接口的,即面向接口编程,它的功能和Dao接口的实现类Impl相当,但是他不用写接口实现类,通过namesapce(命名空间)的绑定直接通过id找到相应方法,执行相应的SQL语句。
比如我目前写是 com.yhn.mapper.UserMapper 那么可以通俗的理解为,我这个mybatis 的映射文件是只属于 com.yhn.mapper
包下的 UserMapper 使用
如何更加明朗的看待? 可以下载一个idea 插件 MybatisX 插件
装上插件后的效果,蓝红鸟以线为例,namespese 对应的是包下面的接口
mybatis标签的意思
<select></select> <!--表示这个是一个SQL查询<==> SELECT-->
<insert></insert> <!--表示这个是一个SQL新增<==> INSERT-->
<update></update> <!--表示这个是一个SQL修改<==> UPDATE-->
<delete></delete> <!--表示这个是一个SQL删除<==> DELETE-->
你就可以这里理解 你现在要执行什么类型的SQL 你就使用什么样的标签
select insert update delete标签的的属性
id : 代表着 namespace绑定的那个接口的方法
resultType : 代表返回的类型 一般是返回实体类型
后续还有,后面还会继续讲解
为什么UserController 下面的 findById方法里面 有@RequestParam注解?
首先,明确一点,就算这个地方不加,也没有影响,程序依旧能跑起来,并且还能返回值,一切正常
那么? 为什么还需要加上@RequestParam注解?
在这里先介绍 @RequestParam
作用:
@RequestParam:将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)
语法:
@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)
value:参数名
required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
加上@RequestParam 和不加@RequestParam
1.如果加上@RequestParam,
1.1 defaultValue属性可以给参数设置默认值,
1.2 required可以设置参数是否必须传,默认为true
1.3 value可以将前端传来的值的key与你用来接收值的参数进行绑定,无需在意参数名字是否一致
1.4 如果设置了defaultValue属性,那么required默认为false
2.如果不加@RequestParam
2.1 前端传来参数的key必须与你后端接受值的那个参数名一致,不然获取不到值
2.2 后端设置的参数的类型如果是基本数据类型 如 int long 等8中基本类型,并且前端没有传这个参数,那么就会报一个错误
"Optional char parameter ‘xxx’ is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type "
这个意思是说,参数xxx是可选的,但是它本身是个基本类型数据,没有办法被转换成null值,让你考虑它把变为当前基本类型的包装类如Long,Integer等,那么你把它转换成对应的包装类就可以了
2.3 后端设置的参数的类型如果是包装类或者String或者自定义的类那么,那个前端可传可不传,如果不传获取的就是null值
为什么UserMapper 下面的 findById方法里面 有@Param注解?
@Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#userId进行取值给SQL的参数赋值。
其实还有多种方式,但是不推荐,但是后面会提到
测试用例 此文档一律使用ApiPost 当然也可以使用其他的postman、Apifox
因为findAll 接口没有任何参数 所有请求区为空
findById 测试 需要传入一个id 的参数并且设定值
新增
在UserController里面添加
@PostMapping("/insert")
public void insert(@RequestBody User user)
service.insert(user);
在UserService添加
/**
* 新增一条数据
* @author YeHaoNan~
* @date 2/11/2022 23:40
* @param user
*/
void insert(User user);
在UserServiceImpl添加
@Override
public void insert(User user)
mapper.insert(user);
在UserMapper添加
void insert(@Param("user") User user);
在UserMapper.xml里面添加
<insert id="insert" >
insert into user
value (
#user.id,
#user.userName,
#user.age,
#user.address
)
</insert>
测试
点击发送,即可新增成功
因为设置了自动递增,则id序列会自动递增
开发知识,这里为什么实力类要用json格式发送?为什么新增的时候,controller层里面的参数要用@RequestBody注解?
例如一个场景,用户在页面上面填写了信息,要新增的时候,前端就会把对应信息的含义和信息转成一个json字符串,发送到后端,后端来接收,但是,后端要直接接收json字符串,是不能接收的,会引起报错,那么就得需要用到@RequestBody 注解,在参数加上,就可以来处理接受前端传过来的json字符串数据
修改
在UserController里面添加
@PostMapping("update")
public void update(@RequestBody User user)
service.update(user);
UserService
void update(User user);
UserserviceImpl
@Override
public void update(User user)
mapper.update(user);
UserMapper
void update(@Param("user")User user);
Usermapper.xml
<update id="update" >
update user set
username = #user.userName,
age = #user.age,
address = #user.address
where
id = #user.id
</update>
测试
修改后
删除
UserController
@PostMapping("delete")
public void delete(Integer id)
service.delete(id);
UserService
void delete(Integer id);
UserServiceImpl
@Override
public void delete(Integer id)
mapper.delete(id);
UserMapper
void delete(@Param("id") Integer id);
UserMapper.xml
<delete id="delete" >
delete from user where id = #id
</delete>
测试
控制台日志
==> Preparing: delete from user where id = ?
==> Parameters: 20(Integer)
<== Updates: 1
完成了删除
三、MyBatis获取参数值的两种方式(重点,面试常考)
$和#
$和# 区别
MyBatis获取参数值的两种方式:$和#
$的本质就是字符串拼接,#的本质就是占位符赋值
$使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;
但是#使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
#是预编译)处理,是占位符,$是字符串替换,是拼接符
Mybatis在处理#的时候会将sql中的#替换成?号,调用PreparedStatement来赋值
演示
//Controller
@GetMapping("findByUserName")
public User findByUserName(@RequestParam("userName") String userName)
return service.findByUserName(userName);
//Service
User findByUserName(String userName);
//ServiceImpl
@Override
public User findByUserName(String userName)
return mapper.findByUserName(userName);
//Mapper
User findByUserName(@Param("userName") String userName);
测试
当使用#
UserMapper.xml
<select id="findByUserName" resultType="com.yhn.entity.User">
select * from user where username = #userName
</select>
控制台日志输出:
==> Preparing: select * from user where username = ?
==> Parameters: 武光职(String)
<== Columns: id, username, age, address
<== Row: 21, 武光职, 20, 湖北
<== Total: 1
当使用$ 加 ‘’
<select id="findByUserName" resultType="com.yhn.entity.User">
select * from user where username = '$userName'
</select>
控制台输出
JDBC Connection [HikariProxyConnection@1631783826 wrapping com.mysql.cj.jdbc.ConnectionImpl@751e3ee7] will not be managed by Spring
==> Preparing: select * from user where username = '武光职'
==> Parameters:
<== Columns: id, username, age, address
<== Row: 21, 武光职, 20, 湖北
<== Total: 1
当使用$ 不加 ‘’
<select id="findByUserName" resultType="com.yhn.entity.User">
select * from user where username = $userName
</select>
控制台输出
threw exception [Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
### The error may exist in file [G:xxx/xxx/xxx]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from user where username = 武光职
### Cause: java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'] with root cause
java.sql.SQLSyntaxErrorException: Unknown column '武光职' in 'where clause'
意思就是SQL语法错误
Unknown column ‘武光职’ in ‘where clause’
意思是找不到
那么为什么会有这样的情况呢?
那是因为 # 会在自动在值两旁加上 ‘’ 而$并不会
也就是跟前面提到的 :
#使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
而如果是 int类型呢?
在来测试 UserController 下的 findById 方法
<!-- User findById(@Param("id") Integer id); -->
<select id="findById" resultType="com.yhn.entity.User">
select * from user where id = $id
</select>
控制台输出
==> Preparing: select * from user where id = 1
==> Parameters:
<== Columns: id, username, age, address
<== Row: 1, UZI, 19, 上海
<== Total: 1
照样能输出结果 因为此时是个int类型的 在SQL语句中 int类型的数据 本身就不需要 ‘’ 而日期 字符串类型就需要,否则就会报错
$和# 的使用技巧
性能考虑
因为预编译语句对象可以重复利用,把一个sql预编译后产生的PreparedStatement对象缓存下来,下次对于同一个sql,可以直接使用缓存的PreparedStatement对象,mybatis默认情况下,对所有的sql进行预编译,这样的话#的处理方式性能会相对高些。
安全考虑
如果作为条件变量的话,那么使用 # 更安全
性能不做案例,下面做安全的案例
这是一条用户的账号、密码数据
当用户登录,我们验证账号密码是否正确时用这个sql:
select * from user where username=$username and password=$password
显然这条sql没问题可以查出来,但是如果有人不知道密码但是想登录账号怎么办
我们不需要填写正确的密码:
username=yyy ; password=1 or 1=1,sql执行的其实是
select * from user where username='yyy' and password=1 or 1 =1
注意:这里的yyy外面的单引号不是 符 号 提 供 的 。 符号提供的。 符号提供的。没有这个功能,可以是sql手动拼接的,这里前后逻辑可能并不严密,但是sql入去最简单的例子就是这样。
所以# 更安全 因为他会自动添加单引号
select * from user where username=#username and password=#password
username=yyy ; password=1 or 1=1,sql执行的其实是
此时password密码直接错误,别人进不去
如何选择使用 #和$呢?
表名、order by的排序字段作为变量时,使用$。
能使用#的时候尽量使用#
我是程序员小孟,欢迎点赞关注!持续更新干货!
以上是关于一天快速掌握Mybaits[一]的主要内容,如果未能解决你的问题,请参考以下文章
SSM springmvc mybaits websocket 服务器框架