当前位置: 首页 > news >正文

Mybatis操作数据库(入门)

一、什么是Mybatis

Mybatis是一款优秀的持久层框架,用于简化JDBC的开发。

其中的持久层指的就是持久化操作的层,通常指数据库访问层(dao),是用来操作数据库的。

简单来说Mybatis是更简单完成程序和数据库交合的框架,也就是更简单的操作和读取数据库的工具。


二、Mybatis入门

(一)准备工作

1、创建工程

在创建SpringBoot工程中,勾选SQL中的MyBatis Framework和MySQL Driver选项。

工程创建完成之后,会自动在pom.xml文件中添加Mybatis的依赖包和驱动包。

<!--Mybatis 依赖包-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency>
<!--mysql驱动包-->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>

2、数据准备

创建用户表,并创建对应的实体类User。

-- 使用数据库
USE mybatis_test;-- 创建表[用户表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常,1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;-- 添加用户信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

创建对应的实体类UserInfo:

package com.bite.mybatis.model;import lombok.Data;@Data
public class UserInfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;}

(二)配置数据库连接

Mybatis中要连接数据库,需要数据库相关参数配置。

在application.yml文件中,配置内容如下:

#数据库配置datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver

(三)写持久层代码

在项目中,先创建出持久层接口UserInfoMapper:

package com.bite.mybatis.mapper;import com.bite.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;//定义一个查询接口,交给Spring来管理
@Mapper
public interface UserInfoMapper {//表明这个方法对应的是这条sql语句@Select("select * from user_info")List<UserInfo> selectAll();
}
  • Mybatis的持久层接口规范一般都叫xxxMapper。
  • @Mapper注解:表示的是Mybatis中的Mapper接口,程序运行时,框架会自动生成接口的实现类对象,并交给Spring的IOC容器管理。
  • @Select注解:代表的就是select查询,表示下面这个方法的作用对应到数据库就是这条sql语句。

(四)测试

关于生成测试代码,我们在需要测试的类中点击鼠标右键,选择Generrate中的Test,再勾选要测试的方法即可生成测试代码。

package com.bite.mybatis.mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid selectAll() {System.out.println(userInfoMapper.selectAll());}
}

@Autowired注解:将Spring中管理的UserInfoMapper对象注入到当前对象中。

测试结果:

显示了表中所有数据。


三、Mybatis的基本操作

(一)打印日志

我们只需要在配置文件中添加如下:

如果是在yml文件中:

mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

如果是在properties文件中:

#指定mybatis输出日志的位置,输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

配置了文件之后,对数据库进行操作会有类似于下面语句:


(二)数据传递

当我们进行查询时,如查找id=4的用户,对应的sql语句就是:select*from user_info where id=4

 @Select("select * from user_info")List<UserInfo> selectAll();

但是这样代码就被写死了,解决方法是在selectAll方法中添加一个参数,讲方法中的参数传递给sql语句。

在Mybatis中,使用#{}的方式获取方法中的参数到sql语句中。

@Select("select * from user_info where id = #{id}")UserInfo selectAllById(Integer id);

进行测试:

@Testvoid selectAllById() {System.out.println(userInfoMapper.selectAllById(3));}

⭐️另外,由于数据库相关参数命名的规范与java不同,也可以通过@param注解,设置参数的别名:

@Select("select * from user_info where id = #{id}")UserInfo selectAllById(@Param("id") Integer number);

(三)增(insert)

在UserInfoMapper接口中:

@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);

测试代码:

@Testvoid insert() {UserInfo userInfo=new UserInfo();userInfo.setUsername("zhaoLiu");userInfo.setPassword("zhaoLiu");userInfo.setGender(2);userInfo.setAge(21);userInfo.setPhone("18612340005");userInfoMapper.insert(userInfo);}

返回主键

insert语句默认返回的是受影响的行数。

但是在有些情况下,数据插入之后,还需要有后续的关联操作,需要获取到新插入数据的id。

如果想要拿到自增id,就需要在Mapper接口的方法上添加一个Options的注解。

  • useGeneratedKeys:使 MyBatis 使用 JDBC 的getGeneratedKeys方法获取数据库内部生成的主键(如 MySQL、SQL Server 的自动递增字段),默认值false
  • keyProperty:指定唯一识别对象的属性,MyBatis 会用getGeneratedKeys的返回值或insert语句的selectKey子元素设置其值,默认未设置。

(四)删(delete)

在UserInfomapper接口中:

@Delete("delete from user_info where id = #{id}")
void delete(Integer id);

测试代码:

@Testvoid delete() {userInfoMapper.delete(1);}


(五)改(update)

在UserInfoMapper接口中:

@Update("update user_info set username=#{username} where id=#{id}")
void update(UserInfo userInfo);

(六)处理数据库与java命名规范不一致的问题

1、起别名

在sql语句中,给列名起别名,保持别名和实体类属性名一样。

@Select("select id, username, `password`, age, gender, phone, delete_flag as deleteFlag, " +"create_time as createTime, update_time as updateTime from user_info")
public List<UserInfo> queryAllUser();

如果sql语句太长,可以通过“+”相连接。


2、结果映射

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info")
@Results({@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser();

如果有其他sql也需要复用这个映射关系,可以给这个Results定义一个名称:

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info")//在此处定义了id,value是映射关系
@Results(id = "resultMap", value = {@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser();@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time " +"from user_info where id = #{userid} ")//此处复用了上面的Results,value为上面的id
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);

3、开启驼峰命名(推荐)

通常数据库列使用蛇形命名法进行命名 (下划线分割各个单词),而 Java 属性一般遵循驼峰命名法约定。
为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。

mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换

在yml文件中做上述配置,java代码不需要做任何处理。


四、Mybatis XML配置文件

Mybatis的开发有两种方式:

  • 注解
  • XML

上面学习的是注解的方式,主要是用来完成一些简单的增删查改的功能,如果要实现复杂的sql功能,建议使用XML来配置映射语句,也就是将sql语句写在XML配置文件中。

Mybatis XML的方式需要以下两步:

  • 配置数据库连接字符串和Mybatis
  • 写持久层代码

(一)配置数据库连接字符串和Mybatis

如果是在application.yml文件中:

mybatis:# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件mapper-locations: classpath:mapper/**Mapper.xml

(二)写持久层代码

持久层代码分两部分:

  • 方法定义interface
  • 方法实现xml

1、添加Mapper接口

package com.bite.mybatis.mapper;import com.bite.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;
@Mapper
public interface UserInfoMapperXML {}

2、添加UserInfoMapperXML.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.bite.mybatis.mapper.UserInfoMapperXML"></mapper>

在application.yml文件中与UserInfoMapperXML.xml文件的路径对应。

我们可以下载一个MybatisX的插件,这样可以在UserInfoMapperXML.java文件和UserInfoMapperXML.xml文件对应处相互跳转。


(三)测试代码

Mapper接口代码:

package com.bite.mybatis.mapper;import com.bite.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;
@Mapper
public interface UserInfoMapperXML {List<UserInfo> selectAll();}

XML文件代码:

⭐️当我们在mapper接口下写出方法,可以自动在xml文件中生成带select标签的语句,在其中可以写对应的SQL语句。

<?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.bite.mybatis.mapper.UserInfoMapperXML"><!--    查询数据--><select id="selectAll" resultType="com.bite.mybatis.model.UserInfo">select * from user_info</select></mapper>

此时mapper接口中的selectAll方法就与xml文件中select标签中的语句相绑定。

测试代码:

package com.bite.mybatis.mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperXMLTest {@Autowiredprivate UserInfoMapperXML userInfoMapperXML;@Testvoid selectAll() {System.out.println(userInfoMapperXML.selectAll());}
}

针对使用XML文件对Mybatis的操作,增删查改的操作与演示一致。


(四)其他查询操作

1、#{ }与${ }

Mybatis参数赋值有两种方式,前面已经提到过#{ }进行赋值,接下来来看二者的区别。

(1)先看Integer类型的参数

mapper接口:

//探究#{ }和${ }的区别@Select("select username, `password`, age, gender, phone from user_info where id= #{id}")UserInfo queryById(Integer id);@Select("select username, `password`, age, gender, phone from user_info where id= ${id}")UserInfo queryById2(Integer id);

测试代码:

@Testvoid queryById() {System.out.println(userInfoMapper.queryById(1));}@Testvoid queryById2() {System.out.println(userInfoMapper.queryById2(1));}

queryById方法结果:

queryById2方法结果:

从以上结果日志中可以看出,输出的sql语句:

//使用#{}
select username, `password`, age, gender, phone from user_info where id= ?//使用${}
select username, `password`, age, gender, phone from user_info where id= 1

在使用#{}时,输入的参数并没有在后面进行拼接,id的值使用?进行占位,这种SQL,被称之为“预编译SQL”。

而使用${},则被称为“即时编译”。


(2)String类型的参数

mapper接口:

@Select("select username, `password`, age, gender, phone from user_info where username= #{name} ")UserInfo queryByName(String name);@Select("select username, `password`, age, gender, phone from user_info where username= ${name} ")UserInfo queryByName2(String name);

测试代码:

 @Testvoid queryByName() {System.out.println(userInfoMapper.queryByName("admin"));}@Testvoid queryByName2() {System.out.println(userInfoMapper.queryByName2("admin"));}

queryByName方法结果:

queryByName2方法结果:

    如果将queryByName2方法改为:

    //在${}两端加上引号:
    @Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")UserInfo queryByName2(String name);

    从上面两个例子可以看出:

    • #{} 使用的是预编译 SQL,通过 ? 占位的方式,提前对 SQL 进行编译,然后把参数填充到 SQL 语句中。
    • #{} 会根据参数类型,自动拼接引号 ''${} 会直接进行字符替换,一起对 SQL 进行编译。如果参数为字符串,需要加上引号 ''
    ⭐️(3)#{}相对于${}的优点
    • 能提高效率:绝大多数情况下,某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的语法解析、SQL 优化、SQL 编译等,则效率就明显不行了。预编译 SQL,编译一次之后会将编译后的 SQL 语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。
    • 防止SQL 注入:SQL注入是通过操作输入的数据来修改事先定义好的 SQL 语句,以达到执行代码对服务器进行攻击的方法。由于没有对用户输入进行充分检查,而 SQL 又是拼接而成,在用户输入参数时,在参数中添加一些 SQL 关键字,达到改变 SQL 运行结果的目的,也可以完成恶意攻击。

    2、排序功能

    从上面的例子中,可以得出结论:

    ${} 会有 SQL 注入的风险,所以我们尽量使用#{}完成查询。既然如此,是不是${}就没有存在的必要性了呢?当然不是。接下来我们看下${}的使用场景。

    当我们想使用排序功能时:

    @Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from user_info order by id ${sort} ")
    List<UserInfo> queryAllUserBySort(String sort);

    使用 ${sort} 可以实现排序查询,而使用 #{sort} 就不能实现排序查询了。

    注意:此处 sort 参数为 String 类型,但是 SQL 语句中,排序规则是不需要加引号 '' 的,所以此时的 ${sort}也不加引号。

    ⭐️如果使用#{}:

    可以发现,当使用 #{sort} 查询时,asc 前后自动给加了引号,导致 sql 错误。

    #{} 会根据参数类型判断是否拼接引号 ''如果参数类型为 String,就会加上 引号。

    3、like查询

    like 使用 #{} 报错:

    @Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from user_info where username like '%#{key}%' ")
    List<UserInfo> queryAllUserByLike(String key);

    把 #{} 改成 可以正确查出来,但是{} 存在 SQL 注入的问题,所以不能直接使用 ${}。

    解决办法:使用 mysql 的内置函数 concat () (将字符串拼接)来处理,实现代码如下:

    @Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from user_info where username like concat('%',#{key},'%')")
    List<UserInfo> queryAllUserByLike(String key);

    http://www.dtcms.com/a/597675.html

    相关文章:

  • 高阅读量博文:抓住“痛点”与“热点”/ 高下载量资源:提供“成品”与“利器”
  • css之flex属性
  • 网站建设时间计划广州高端网站设计公司排名
  • 怎么用自己的网站做邮箱wordpress 设计主题
  • 【LlamaIndex】LlamaIndex与Milvus实现知识库
  • 如何查看网站是谁建设的茂名住房和城乡建设厅网站
  • 从图像导数到边缘检测:探索Sobel与Scharr算子的原理与实践
  • vue路由路径加上自动退出登录
  • 推图制作网站无法连接到wordpress
  • Java面试题2:Java线程池原理
  • 小企业网站源码wordpress主题放在那个文件夹
  • HarmonyOS动画性能提升:renderGroup缓存与属性动画优化
  • 玉器企业网站源码wordpress外观小工具
  • kotlin扩展函数是如何实现的
  • 农业科技工作服务站建站模板让网站不要保存密码怎么做
  • 深入学习Redis():Redis内存模型
  • 输出模式下,上下拉电阻不起作用的原因:
  • 如何让PVC制品更安全?稀土抑烟剂助力安全防护
  • Python基础教学-可迭代的(Iterable)和迭代器(iterator)的区别-由Deepseek产生
  • 摄影作品网站推荐网站全屏代码
  • 网站开发中加入cad功能一个空间能否做两个网站
  • SM2 vs RSA/ECC:双算法 SSL 证书的性能对比与优化方案
  • 使用微信小程序实现多格验证码效果
  • 用node.js可以做网站吗网站的发布与推广怎么写
  • 【JavaEE进阶】-- 加密算法
  • 58同城本地版下载优化设计高中
  • 入门|利用 Highcharts 的 ES6/ESM 模块安装方案
  • 【NGINX的学习】
  • 重庆南坪网站建设公司学校网站总务建设
  • C++ yjx