Java EE 进阶:MyBatis

MyBatis是一个优秀的持久化框架,用于简化JDBC的开发。

持久层就是持久化访问的层,就是数据访问层(Dao),用于访问数据库的。

MyBatis使用的准备工作

创建项目,导入mybatis的启动依赖,mysql的驱动包

在pom文件中生成依赖

java 复制代码
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

首先我们准备一下数据库的数据

创建user_info表

sql 复制代码
DROP DATABASE IF EXISTS mybatis_test;
 CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
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
',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0',
 `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' );

配置数据库的连接(.yml文件中的配置)

创建数据库表中对应的实体类

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

注:数据库中字段全部都使用小写,单词之间用_来连接,Java中的属性使用小驼峰的方式

注:由于引入了lombok依赖,所以直接使用@Date注解,不需要再去写Getter和Setter方法

创建在持久层的接口,在MyBatis中持久层中的接口一般使用 xxxMapper 的命名方法

我们创建了UserInfoMapper接口

注:@Mapper注解就是表明该接口是MyBatis中的Mapper接口

单元测试

在创建出来的SpringBoot⼯程中,在src下的test⽬录下,已经⾃动帮我们创建好了测试类,我们可以 直接使⽤这个测试类来进⾏测试

单元测试是开发人员进行的测试,与测试人员无关。

进行测试的方法

生成测试类的方法

找到你需要生成测试类的Mapper接口的方法上,右键generate,点击Test

在你需要测试的方法上打 √ ,然后点ok就会自动生成了

MyBatis中的基础操作(注解)

打印日志

只需要在配置文件中配置即可

就可以直接看到sql的执行过程,参数传递,执行结果。

参数的传递

查询对象的方式(select)

id为固定值的时候
java 复制代码
@Select("select * from user_info where id=3")
    List<UserInfo> selectAllById();
java 复制代码
@Test
    void selectAllId() {
        System.out.println(userInfoMapper.selectAllById());
    }
id为动态的数值

使用#{}的方式获取方法中的参数

java 复制代码
    @Select("select * from user_info where id=#{id}")
    UserInfo selectAllById3(Integer id);
java 复制代码
@Test
    void selectAllById3() {
        System.out.println(userInfoMapper.selectAllById3(1));
    }
传递多个参数
java 复制代码
 @Select("select * from user_info where username=#{username} and password=#{password}")
    UserInfo selectAllByUserNameAndPassWord(String username,String password);
java 复制代码
 @Test
    void selectAllByUserNameAndPassWord() {
        System.out.println(userInfoMapper.selectAllByUserNameAndPassWord("zhangsan","zhangsan"));
    }
可以使用@Param注解进行参数的绑定(重命名)
java 复制代码
@Select("select * from user_info where username=#{aa} and password=#{bb}")
    UserInfo selectAllByUserNameAndPassWord2(@Param("aa") String username,@Param("bb") String password);
java 复制代码
@Test
    void selectAllByUserNameAndPassWord2() {
        System.out.println(userInfoMapper.selectAllByUserNameAndPassWord2("zhangsan","zhangsan"));
    }

增 (Insert)

java 复制代码
@Insert("insert into user_info (username,password,age) values (#{username},#{password},#{age})")
    Integer insert(UserInfo userInfo);
java 复制代码
@Test
    void insert() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("lisi");
        userInfo.setPassword("lisi");
        userInfo.setAge(1);
        Integer result=userInfoMapper.insert(userInfo);
        System.out.println("新增行数"+result);
    }

返回主键

获得自增Id

java 复制代码
//获取自增Id
    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("insert into user_info (username,password,age) values (#{username},#{password},#{age})")
    Integer insert1(UserInfo userInfo);
java 复制代码
@Test
    void insert1() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("lisi");
        userInfo.setPassword("lisi");
        userInfo.setAge(1);
        Integer result=userInfoMapper.insert1(userInfo);
        System.out.println("新增行数"+result+"Id"+userInfo.getId());
    }

删 (delete)

java 复制代码
@Delete("delete from user_info where id =#{id}")
    Integer deleteUser(Integer id);
java 复制代码
@Test
    void deleteUser() {
        userInfoMapper.deleteUser(13);
    }

改(update)

java 复制代码
@Update("update user_info set delete_flag= #{deleteFlag},phone= #{phone} where id= #{id}")
    Integer updateUser(UserInfo userInfo);
java 复制代码
@Test
    void updateUser() {
        UserInfo userInfo=new UserInfo();
        userInfo.setDeleteFlag(1);
        userInfo.setPhone("123");
        userInfo.setId(11);
        Integer result=userInfoMapper.updateUser(userInfo);
        System.out.println(result);
    }

查询的需要注意的问题

当我们进行查询的时候,我们会发现某些字段是没有进行赋值的,只有Java对象属性和数据库字段⼀模⼀样时,才会进⾏赋值

原因:MyBatis 在执行查询后,会将数据库中的字段值映射(映射 = 赋值)到 Java 实体类对象的属性中。但是,如果 数据库字段名和 Java 对象属性名不一致,MyBatis 默认不会自动进行映射赋值

解决办法:

1.起别名

java 复制代码
@Select("select id, username,`password`, age, gender,  phone, " + 
"delete_flag as deleteFlag, create_time as createTime, update_time as updateTime" + 
" from user_info")

2.结果映射

java 复制代码
@Results(id = "BaseMap", value = {
            @Result(column = "delete_flag", property = "deleteFlag"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
@Select("select * from user_info")

3.添加驼峰命名的配置文件

设置为true

MyBatis XML配置文件

MyBatis的开发方式一般有两种,注解 & XML

现在我们来学习XML的开发方式

配置mybatis

Spring Boot 集成 MyBatis 时指定 Mapper XML 文件的位置,告诉 MyBatis 去哪里加载 SQL 映射文件

添加Mapper接口

添加 .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.blame.springmybatis.mapper.UserInfoMapperXML">


    <select id="selectUserAll" resultType="com.blame.springmybatis.model.UserInfo">
        select * from user_info
    </select>

</mapper>

单元测试

java 复制代码
@SpringBootTest
class UserInfoMapperXMLTest {

    @Autowired
    private UserInfoMapperXML userInfoMapperXML;

    @Test
    void selectUserAll() {
        userInfoMapperXML.selectUserAll().stream().forEach(x-> System.out.println(x));
    }
}

XML中的增删改查

增(insert)

java 复制代码
 Integer insertUser(UserInfo userInfo);
XML 复制代码
 <insert id="insertUser">
        insert into user_info (username, `password`, age) VALUES (#{username}, #{password}, #{age})
    </insert>
java 复制代码
@Test
    void insertUser() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("xiaohei");
        userInfo.setPassword("123");
        userInfo.setAge(11);
        Integer result=userInfoMapperXML.insertUser(userInfo);
        System.out.println("增加行数"+result+"Id"+userInfo.getId());
    }

使用@Param注解进行重命名

java 复制代码
Integer insertUser2(@Param("UserInfo") UserInfo userInfo);
java 复制代码
<insert id="insertUser2">
        insert into user_Info (username, `password`, age) VALUES (#{username}, #{password}, #{age})
    </insert>
java 复制代码
@Test
    void insertUser2() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("hei");
        userInfo.setPassword("15");
        userInfo.setAge(13);
        Integer result=userInfoMapperXML.insertUser(userInfo);
        System.out.println("增加行数"+result+"Id"+userInfo.getId());
    }

自增Id

XML 复制代码
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
 insert into user_info (username, `password`, age, gender, phone) values
 (#{userInfo.username},#{userInfo.password},#{userInfo.age},#
 {userInfo.gender},#{userInfo.phone})
 </insert>

删(delete)

java 复制代码
Integer deleteUser(Integer id);
XML 复制代码
<delete id="deleteUser">
        delete from user_info where id =#{id}
    </delete>
java 复制代码
   @Test
    void deleteUser() {
        userInfoMapperXML.deleteUser(16);
    }

改(update)

XML 复制代码
<update id="updateUser">
        update user_info set  password= #{password} , age=#{age} where id= #{id}
    </update>
java 复制代码
Integer updateUser(String password,Integer age,Integer id);
java 复制代码
@Test
    void updateUser() {
        UserInfo userInfo=new UserInfo();
        Integer result=userInfoMapperXML.updateUser("123",88,9);
    }

查(select)

java 复制代码
List<UserInfo> selectUserAll();
XML 复制代码
<select id="selectUserAll" resultType="com.blame.springmybatis.model.UserInfo">
        select * from user_info
    </select>

其他查询

联合查询(多表查询)

在数据库中导入表

sql 复制代码
DROP TABLE IF EXISTS articleinfo;
CREATE TABLE articleinfo (
 id INT PRIMARY KEY auto_increment,
 title VARCHAR ( 100 ) NOT NULL,
 content TEXT NOT NULL,
 uid INT NOT NULL,
 delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT,
 create_time DATETIME DEFAULT now(),
 update_time DATETIME DEFAULT now() 
) DEFAULT charset 'utf8mb4';

 INSERT INTO articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正⽂', 1 );

配置对应的实体类

java 复制代码
@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer uid;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
    private String username;
    private Integer age;
}

定义接口

java 复制代码
@Mapper
public interface ArticleInfoMapper {

    ArticleInfo selectArticleById(Integer id);
}

配置 .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.blame.springmybatis.mapper.ArticleInfoMapper">

    <select id="selectArticleById" resultType="com.blame.springmybatis.model.ArticleInfo">
        select ta.*,tb.username,tb.age from article_info ta left 
join user_info tb on ta.uid = tb.id where ta.id=#{id}

    </select>
</mapper>

测试类

java 复制代码
@SpringBootTest
class ArticleInfoMapperTest {
    @Autowired
    private ArticleInfoMapper articleInfoMapper;

    @Test
    void selectArticleById() {
        articleInfoMapper.selectArticleById(1);
    }
}

#{ }和${ }

这是mybatis中非常重要的两个占位符

首先我们看一下#{}

java 复制代码
@Select("select username,`password`,age,gender,phone from user_info where id=#{id}")
    UserInfo queryById(Integer id);
java 复制代码
@Test
    void queryById() {
        System.out.println(userInfoMapper.queryById(2));
    }

通过观察我们的sql语句,我们发现我们输入的参数 2 ,并没有在语句后面显示,而是用 ? ,我们把这种叫做预编译SQL

我们再观察一下${}

java 复制代码
    @Select("select username,`password`,age,gender,phone from user_info where id=${id}")
    UserInfo queryById2(Integer id);
java 复制代码
@Test
    void queryById2() {
        System.out.println(userInfoMapper.queryById2(2));
    }

我们可以发现参数直接是拼在SQL语句中了

我们再把参数换成String类型

java 复制代码
@Select("select username,`password`,age,gender,phone from user_info where username=#{username}")
    UserInfo queryById3(String username);

@Select("select username,`password`,age,gender,phone from user_info where username=${username}")
    UserInfo queryById4(String username);
java 复制代码
@Test
    void queryById3() {
        System.out.println(userInfoMapper.queryById3("zhangsan"));
    }

    @Test
    void queryById4() {
        System.out.println(userInfoMapper.queryById4("zhangsan"));
    }

我们先来看一下#{}的测试结果

再看一下${}的测试结果

我们发现报错了,说SQL语法异常

这次的参数是直接拼在SQL语句的后面,没有自动给username添加 ' '

修改后,测试成功

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

通过上面这个例子,我们可以发现#{}使用的是预编译SQL,通过?进行占位,提前对SQL进行编译,将参数填充到SQL语句中,#{}会根据参数类型,自动拼接引号 ' '

${}会直接进行字符的替换,一起对SQL进行编译,如果是字符串,得加上引号' '

SQL语句的执行流程

1.语法解析 2.SQL优化 3.SQL编译 4.SQL执行

#{} 和${}区别

#{}的性能更高

${}会出现SQL注入的风险

SQL注入:是通过操作输⼊的数据来修改事先定义好的SQL语句,以达到执⾏代码对服务器进⾏攻击的 ⽅法

SQL注入的代码演示

java 复制代码
@Select("select username, `password`, age, gender, phone from user_info 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);
    }

排序功能

当我们表中数据按Id逆序进行排序,发现${}排序成功

java 复制代码
 @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);
java 复制代码
@Test
    void queryAllUserBySort() {
        System.out.println(userInfoMapper.queryAllUserBySort("desc"));
    }

但我们发现#{} 没有成功,进行了报错。

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time,update_time " +
            "from user_info order by id #{sort} ")
    List<UserInfo> queryAllUserBySort2(String sort);

SQL语法错误异常

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

like的使用

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

这里 '%#{key}%' 是错误写法,因为 #{} 在 MyBatis 中是 参数占位符,它不会在 SQL 字符串内部被当作字符串拼接,而是被替换成 ?

我们可以使用从concat()

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

数据库连接池

预先创建好的一批数据库连接,当应用程序要访问数据库时,从池中获取连接,操作完成后再归还,而不是每次都重新建立和关闭连接

常用的的数据连接池

HikariCP(Spring Boot默认):性能最快、轻量

Druid(阿里巴巴):功能丰富、监控强大

C3P0 :比较落后

DBCP :使用少

切换数据库连接池

如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要引⼊相关依赖即可

java 复制代码
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-3-starter</artifactId>
 <version>1.2.21</version>
 </dependency>

希望能对大家有所帮助!!!!!

相关推荐
血手人屠喵帕斯7 小时前
MyBatis 的一次缓存与二次缓存
java·spring·oracle
hxung8 小时前
常用的 MyBatis 标签及其作用
mybatis
dingdingfish10 小时前
Oracle 数据库安全评估(DBSAT)简明过程
oracle·database·security·assessment·dbsat
星月前端12 小时前
Spring Boot定时任务设置与实现
java·spring boot·mybatis
wjm04100612 小时前
mysql
数据库·mysql·oracle
码农幻想梦15 小时前
试验一 mybatis 入门操作
mybatis
kiwixing15 小时前
Oracle ASM 磁盘组冗余策略
java·linux·运维·数据库·c++·python·oracle
八股文领域大手子15 小时前
Redis Lua脚本实现令牌桶限流算法
java·数据库·redis·算法·junit·mybatis·lua
小丁爱养花15 小时前
MyBatis-Plus:告别手写 SQL 的高效之道
java·开发语言·后端·spring·mybatis
百锦再17 小时前
Oracle数据库性能优化全攻略:十大关键方向深度解析与实践指南
数据库·oracle·性能优化·并发·索引·分区