【JavaEE 进阶(六)】Mybatis操作数据库

❣博主主页: 33的博客
▶️文章专栏分类:JavaEE◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你了解更多进阶知识

在这里插入图片描述

1.前言

在应用分层学习时,我们了解到web应⽤程序⼀般分为三层,即:Controller、Service、Dao。之前的案例中,请求流程如下:浏览器发起请求,先请求Controller,Controller接收到请求之后,调用Service进⾏业务逻辑处理,Service再调⽤Dao,但是Dao层的数据是Mock的,真实的数据应该从数据库中读取。我们知道在java中可以通过JDBC来操作数据库,但这样操作是非常复杂的,那在Spring Boot中有更简便的方法吗?接下来我们就进行学习吧!

2.JDBC操作

我们先了解一下通过JDBC来操作数据库:

  • 创建数据库连接池DataSource
  • 通过数据库连接池获取数据库连接Connection
  • 编写带有?占位符的sql语句
  • 替换占位符,指定要替换的数据库字段类型,要替换的值。
  • 使用Statement执行SQL语句
  • 查询操作:返回结果ResultSet,更新操作:返回更新的数据量
  • 处理结果集
  • 释放资源
//创建数据库
create database if not exists library default character set utf8mb4;   
use library;-- 
//创建表
create table if not exists soft_bookrack (
  book_name varchar(32) NOT NULL,
  book_author varchar(32) NOT NULL,
  book_isbn varchar(32) NOT NULL primary key
 );

JDBC具体代码(以新增为例)

public class SimpleJdbcOperation {
  private final DataSource dataSource;
  public SimpleJdbcOperation(DataSource dataSource) {
        this.dataSource = dataSource;
    }
//添加⼀本书
  public void addBook() {
    Connection connection = null;
    PreparedStatement stmt = null;
    try {
//获取数据库连接  
connection=dataSource.getConnection();         
//创建语句
stmt=connection.prepareStatement("insert into soft_bookrack (book_name, book_author, book_isbn) values (?,?,?);");
 //参数绑定
stmt.setString(1, "Spring in Action");
stmt.setString(2, "Craig Walls");
stmt.setString(3, "9787115417305");
 //执⾏语句
 stmt.execute();
 } catch (SQLException e) {
//处理异常信息
 } finally {
//清理资源
 try {
  if (stmt != null) {
   stmt.close();
   }
    if (connection != null) {
   connection.close();
     }
     } catch (SQLException e) {
                //
     }
   }
 }

从上诉代码可见,对于JDBC来说,整个操作是非常麻烦的,,我们不但要拼接每⼀个参
数,而且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接等,而所有的这些操作步骤都需要在每个⽅法中重复书写.那有没有⼀种方法,可以更简单、更方便的操作数据库呢?当然是有的,那就是Mybatis,它可以帮助我们更方便的操作数据库。

3.Mybatis

Mybatis是持久层框架,用于简化JDBC的开发。持久层就是指持久化操作的层,通常指数据访问层,用来操作数据库的。
在这里插入图片描述
简单来说Mybatis是更简单完成程序和数据库交互的框架。

3.1Mybatis入门

3.1.1准备工作

创建Spring boot工程,并导入mybatis的依赖和mysql驱动包
在这里插入图片描述
项目创建完成之后,会自动在pom文件中导入相关依赖。
在这里插入图片描述
数据准备:

CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
USE mybatis_test;
CREATE TABLE `userinfo` (
 `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(),
 PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 
//添加⽤⼾信息
 INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
 VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
 INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
 VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
 INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
 VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
 INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
 VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

对应实体类:
与表中字段名一致

import lombok.Data;
 import java.util.Date;
 @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;
 private Date createTime;
 private Date updateTime;
 }

3.1.2配置数据库连接

spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
 username: root//用户名
 password: root//密码
 driver-class-name: com.mysql.cj.jdbc.Driver

3.1.2写持久层代码

@Mapper
public interface UserInfoMapper {
 //查询所有⽤⼾
 @Select("select username, `password`, age, gender, phone from userinfo")
 public List<UserInfo> queryAllUser();
}

进行单元测试,自动生成
在这里插入图片描述

@SpringBootTest
class UserInfoMapperTest {
 @Autowired
 private UserInfoMapper userInfoMapper;
 @Test
 void queryAllUser() {
 List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
 System.out.println(userInfoList);
 }
}

在这里插入图片描述

3.2MyBatis基础操作

3.2.1打印日志

在Mybatis当中我们可以借助⽇志, 查看到sql语句的执⾏、执⾏传递的参数以及执⾏结果在配置⽂件中进⾏配置即可:

mybatis:
 configuration: # 配置打印 MyBatis⽇志
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在这里插入图片描述

3.2.2参数传递

如果要查找id为4的用户信息可以写为:

@Select("select username, `password`, age, gender, phone from userinfo where id= 4 ")
UserInfo queryById();

但是这样就把id写死了,不能修改,那么我们可以使用参数来优化:

@Select("select username, `password`, age, gender, phone from userinfo where id= #{id} ")
UserInfo queryById(Integer id);

当我们调用queryById时,传递不同的id值就可以查询出不同的信息。

3.2.3增

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

测试:

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

3.2.3删

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

测试

@Test
void delete() {
 userInfoMapper.delete(4);
}

3.2.4改

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

测试

@Test
void  update() {
 UserInfo userInfo = new UserInfo();
 userInfo.setUsername("zhaoliu666");
 userInfo.setId(5);
 userInfoMapper.insert(userInfo);
}

3.2.5查

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
List<UserInfo> queryAllUser();

测试

@Test
void  queryAllUser() {;
 userInfoMapper.queryAllUser();
}

当我们查询的时候,我们发现有一些数值为空,但数据库中却有值是怎么回事呢?
在这里插入图片描述
MyBatis会根据方法的返回结果进行赋值,方法用对象UserInfo接收结果,就会根据对应UserInfo的属性进行赋值。
在这里插入图片描述
解决办法:
1.起别名

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

2.结果映射

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

3.开启驼峰命名

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

此时代码不用做任何处理,会把update_time转换为:updateTime并赋值

3.3MyBatis XML配置

MyBatis的开发方式有2种,注解和XML上面我们学习的都是通过注解实现的,接下来我们通过XML来实现。

3.3.1配置XML文件

配置XML文件路径,在resources/mapper下面,以Mapper.xml结尾的文件

mybatis:
 mapper-locations: classpath:mapper/**Mapper.xml

3.3.2Mapper接口

mapper文件种添加UserInfoXMlMapper类

@Mapper
public interface UserInfoXMlMapper {
 List<UserInfo> queryAllUser();
}

resources/mapper创建UserInfoXMLMapper.xml类,并添加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.example.demo.mapper.UserInfoMapper">
 <select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select username,`password`, age, gender, phone from userinfo
 </select>
</mapper>

3.3.3增

Integer insertUser(UserInfo userInfo);
<insert id="insertUser">
insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>

3.3.4删

Integer deleteUser(Integer id);
<delete id="deleteUser">
 delete from userinfo where id = #{id}
</delete>

3.3.5改

Integer updateUser(UserInfo userInfo);
<update id="updateUser">
 update userinfo set username=#{username} where id=#{id}
</update>

3.3.6查

List<UserInfo> queryAllUser();
<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select id, username,`password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo
</select>

#{}和${}区别

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

运行结果:
在这里插入图片描述
使用${}是直接进行字符串拼接,但是字符串作为参数时,需要添加’ ‘,使用 ${}不会拼接’ '。
解决

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

在这里插入图片描述
#{}与${}区别
1.性能更高
2.更安全(防止SQL注入)

@Select("select username, `password`, age, gender, phone from userinfo where username= '${name}' ")
List<UserInfo> queryByName(String name);

正常情况下:

@Test
void queryByName() {
 List<UserInfo> userInfos = userInfoMapper.queryByName("admin");
 System.out.println(userInfos);
 }
}

在这里插入图片描述
sql注入:

@Test
void queryByName() {
 List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");
 System.out.println(userInfos);
}

这时结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分。这时就会查询出所有的表中数据。
既然${}会有Sql注入的风险,那么它是否就没有用呢?实现排序,like查询的时候只能使用 ${}不能使用#{},例如:

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

这时候传入的sort不需要引号所以必须要使用${}

4.数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,而不是再重新建⽴⼀个。
没有使用数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQL语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源
使用数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把Connection归还给连接池

优点:

  1. 减少了⽹络开销
  2. 资源重⽤
  3. 提升了系统的性能

5.总结

利用MyBatis进行数据库的增删改查操作,使用驼峰转换时,要注意java中的属性和数据库中属性的对应关系,一般创建数据库时,表必备三字段: id, create_time, update_time,在表查询中, 避免使⽤ * 作为查询的字段列表, 标明需要哪些字段。

下期预告:MyBatis进阶

评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值