【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,更新操作:返回更新的数据量
  • 处理结果集
  • 释放资源
java 复制代码
//创建数据库
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具体代码(以新增为例)

java 复制代码
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文件中导入相关依赖。

数据准备:

sql 复制代码
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' );

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

java 复制代码
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配置数据库连接

java 复制代码
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写持久层代码

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

进行单元测试,自动生成

java 复制代码
@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语句的执⾏、执⾏传递的参数以及执⾏结果在配置⽂件中进⾏配置即可:

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

3.2.2参数传递

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

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

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

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

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

3.2.3增

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

测试:

java 复制代码
@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删

java 复制代码
@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);

测试

java 复制代码
@Test
void delete() {
 userInfoMapper.delete(4);
}

3.2.4改

java 复制代码
@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);

测试

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

3.2.5查

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

测试

java 复制代码
@Test
void  queryAllUser() {;
 userInfoMapper.queryAllUser();
}

当我们查询的时候,我们发现有一些数值为空,但数据库中却有值是怎么回事呢?

MyBatis会根据方法的返回结果进行赋值,方法用对象UserInfo接收结果,就会根据对应UserInfo的属性进行赋值。

解决办法:

1.起别名

java 复制代码
@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.结果映射

java 复制代码
@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.开启驼峰命名

java 复制代码
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结尾的文件

java 复制代码
mybatis:
 mapper-locations: classpath:mapper/**Mapper.xml

3.3.2Mapper接口

mapper文件种添加UserInfoXMlMapper类

java 复制代码
@Mapper
public interface UserInfoXMlMapper {
 List<UserInfo> queryAllUser();
}

resources/mapper创建UserInfoXMLMapper.xml类,并添加XML固定格式

java 复制代码
<?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增

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

3.3.4删

java 复制代码
Integer deleteUser(Integer id);
java 复制代码
<delete id="deleteUser">
 delete from userinfo where id = #{id}
</delete>

3.3.5改

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

3.3.6查

java 复制代码
List<UserInfo> queryAllUser();
java 复制代码
<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>

#{}和${}区别

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

运行结果:

使用${}是直接进行字符串拼接,但是字符串作为参数时,需要添加' ',使用 ${}不会拼接' '。

解决

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

#{}与${}区别

1.性能更高

2.更安全(防止SQL注入)

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

正常情况下:

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

sql注入:

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

这时结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分。这时就会查询出所有的表中数据。

既然${}会有Sql注入的风险,那么它是否就没有用呢?实现排序,like查询的时候只能使用 ${}不能使用#{},例如:

java 复制代码
@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进阶

相关推荐
工业甲酰苯胺4 分钟前
分布式系统架构:服务容错
数据库·架构
独行soc1 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
White_Mountain1 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
Code apprenticeship1 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站1 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
梦想平凡3 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO3 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong4 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
码农老起4 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化