MyBatis操作数据库——入门
MyBatis操作数据库——入门
文章目录
- MyBatis操作数据库——入门
- 什么是MyBatis?
- MyBatis入门
- 准备工作
- 配置数据库连接字符串
- 打印日志
- 单元测试
- 持久层代码
- 查询
- 增加
- 删除
- 修改
- MyBatis XML配置文件
- 配置数据库连接字符串和MyBatis
- 持久层代码
- 添加mapper接口
- 添加UserInfoMapper.xml
- 增加
- 删除
- 更新
- 查询
- 其他查询操作
- 多表查询
- #{} 和 ${}的区别
- 排序功能
- like查询
- 数据库连接
- 介绍
- 使用
- 1. Hikari
- 2. Druid
什么是MyBatis?
- MyBatis是一款优秀的持久层框架,用于简化JDBC的开发
- MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github
在上面我们提到一个词:持久层
-
持久层:指的就是持久化操作的层,通常指数据访问层(dao),是用来操作数据库的
-
简单的来说,MyBatis是更简单用于与数据库交互的框架,也就是说使用更简单的操作对数据库进行读取和操作
MyBatis入门
Mybatis操作数据库的步骤:
- 准备工作(创建springboot工程、数据库表准备、实体类)
- 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
- 编写SQL语句(注解/XML)
- 测试
准备工作
- 创建SpringBoot项目,并导入MyBatis的依赖和MySQL驱动包
- 项目创建完成之后,自动在pom.xml文件中,导入了MyBatis的依赖和MySQL的驱动
配置数据库连接字符串
Mybatis中要连接数据库,需要数据库相关参数配置
- MySQL驱动类
- 登录名
- 密码
- 数据库连接字符串
如果是application.yml文件,配置内容如下:
# 数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
注意事项:
如果使用 MySQL 是5.x之前的使用的是"com.mysql.jdbc.Driver"
,如果是大于5.x使用的是"com.mysql.cj.jdbc.Driver"
。
如果是application.properties文件,配置内容如下:
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=root
打印日志
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果,在配置文件中进行配置即可
mybatis:configuration: # 配置打印 MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
如果是application.properties,配置内容如下:
#指定mybatis输出日志的位置,输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
注意:后续配置项,默认只提供一种,请自行进行配置项转换。
单元测试
在创建出来的SpringBoot项目中,idea已经帮我们创建好测试类了,我们直接点击添加即可
持久层代码
查询
MyBatis语句:
@Select("select username, password, age, gender, phone from user_info where id = 4")public List<UserInfo> queryAllUser();
弊端:上述情况只能固定查询id等于4的情况,假如需要查询其他id的情况,那么id应该为一个动态的数值
解决:我们可以使用#{}的方式获取参数
@Select("select username, password, age, gender, phone from user_info where id = #{id}")public UserInfo queryAllUser(int id);
注意⚠️:
- 我们在上面查询时发现,有几个字段是没有赋值的,只有 Java 对象属性和数据库字段一模一样时,才会进行赋值
原因分析:
当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性
解决办法:
- 起别名
- 结果映射
- 开启驼峰命名
- 起别名
@Select("select create_time as createTime from user_info")LocalDateTime getCreateTime(UserInfo userInfo);
- 结果映射
@Select("select create_time createTime from user_info")@Results(id = "userInfoResultMap", value = {@Result(property = "createTime", column = "create_time")})default List<UserInfo> selectUserInfo() {return null;}
假如后续还想使用这个映射关系的话,可以用Result注解定义一个名称
@Select("select create_time as createTime from user_info")@ResultMap("userInfoResultMap")List<UserInfo> selectUserInfoWithResultMap();
- 驼峰命名(推荐)
通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而 Java 属性一般遵循驼峰命名法约定。
为了在这两种命名方式之间启用自动映射,需要将mapUnderscoreToCamelCase
设置为 true。
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换
驼峰命名规则: abc_xyz => abcXyz
- 表中字段名: abc_xyz
- 类中属性名: abcXyz
增加
MyBatis语句:
@Insert("insert into user_info (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")void insertUser(UserInfo userInfo);
- 常量已经被替换成动态参数了
- 起别名:
- 在 MyBatis 中,@Param注解是用来为方法参数起别名的
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface UserMapper {// 根据用户id查询用户信息,这里使用@Param注解为参数起别名User selectUserById(@Param("id") Long userId);
}
- 返回主键
- useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
- keyProperty:指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)
说人话:
- useGeneratedKeys = true:启用 “获取数据库自增主键” 的功能
- keyProperty = “id”:指定将获取到的主键值赋给实体类的 id 属性
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, age, gender, phone) values (#{userInfo.username},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);
删除
MyBatis语句:
@Delete("delete from user_info where id = #{id}")void deleteUser(@Param("id") Integer id);
修改
MyBatis语句:
@Update("update user_info set username = #{username} where id = #{id}")void updateUser(UserInfo userInfo);
MyBatis XML配置文件
MyBatis开发有两种方式:
- 注解
- XML
上面我们学习了注解的方式,接下来我们学习XML方式
使用XML方式需要以下两步:
- 配置数据库连接字符串和MyBatis
- 写持久层代码
配置数据库连接字符串和MyBatis
如果是application.yml
文件,配置内容如下:
# 数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mybatis:mapper-locations: classpath:mapper/**Mapper.xml
如果是application.properties
文件,配置内容如下:
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=root
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
持久层代码
- 方法定义 Interface
- 方法实现 XXX.xml
添加mapper接口
@Mapper
public interface UserInfoMapper {UserInfo queryAllUser(String username);
}
添加UserInfoMapper.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"></mapper>
增加
用于向数据库插入一条记录,核心标签为 <insert>
<!-- id:与 Mapper 接口中的方法名一致 -->
<insert id="insertUser">INSERT INTO user (username, age, email, create_time)VALUES (#{username}, #{age}, #{email}, #{createTime})
</insert>
说明:
#{属性名}
:MyBatis 的参数占位符,对应实体类User
的属性(如#{username}
对应user.getUsername()
)。- 若需返回自增主键,需添加
useGeneratedKeys
和keyProperty
属性:
插入后,自增主键会自动赋值给<insert id="insertUser" useGeneratedKeys="true" keyProperty="id"><!-- SQL 语句同上 --> </insert>
User
对象的id
属性
删除
用于删除数据库中的一条或多条记录,核心标签为 <delete>
。
<delete id="deleteUserById">DELETE FROM userWHERE id = #{id}
</delete>
说明:
#{id}
:接收传入的主键 ID 参数,通过WHERE
条件定位要删除的记录。- 可根据需求添加更复杂的条件(如批量删除):
<delete id="deleteUsersByIds">DELETE FROM userWHERE id IN (#{ids}) <!-- ids 为逗号分隔的 ID 字符串,如 "1,2,3" --> </delete>
更新
用于修改数据库中的记录,核心标签为 <update>
。
<update id="updateUser">UPDATE userSET username = #{username},age = #{age},email = #{email},update_time = #{updateTime}WHERE id = #{id}
</update>
说明:
SET
后跟随要更新的字段,字段值通过#{属性名}
从参数(通常是User
对象)中获取。WHERE
条件通常使用主键(如id
),确保只更新目标记录,避免批量更新错误。- 可按需更新部分字段(无需更新的字段不写在
SET
中)。
查询
用于从数据库查询数据,核心标签为 <select>
,需指定返回结果类型。
(1)查询单个对象
<!-- resultType:指定查询结果的返回类型(实体类全路径) -->
<select id="selectUserById" resultType="com.example.entity.User">SELECT id, username, age, email, create_time, update_timeFROM userWHERE id = #{id}
</select>
(2)查询多个对象(返回列表)
<!-- 返回列表时,resultType 仍指定单个实体类型,MyBatis 会自动封装为 List -->
<select id="selectAllUsers" resultType="com.example.entity.User">SELECT id, username, age, email, create_time, update_timeFROM user
</select>
(3)条件查询(模糊查询)
<select id="selectUsersByUsername" resultType="com.example.entity.User">SELECT id, username, age, email, create_time, update_timeFROM userWHERE username LIKE CONCAT('%', #{username}, '%')
</select>
解决办法和注解类似:
- 起别名
- 结果映射
- 开启驼峰命名
其中1、3的解决办法和注解一样,不再多说,接下来看下xml如果来写结果映射
Mapper.xml
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo"><id column="id" property="id"></id><result column="delete_flag" property="deleteFlag"></result><result column="create_time" property="createTime"></result><result column="update_time" property="updateTime"></result>
</resultMap><select id="queryAllUser" resultMap="BaseMap">select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info
</select>
说明:
resultType
:必须指定,用于 MyBatis 将查询结果映射为对应的 Java 对象(单个对象或集合中的元素)- 模糊查询:使用
LIKE
关键字,通过CONCAT('%', #{username}, '%')
拼接通配符(避免 SQL 注入风险)
其他查询操作
多表查询
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface ArticleInfoMapper {@Select("SELECT ta.id,ta.title,ta.content,ta.uid,tb.username,tb.age,tb.gender " +"FROM articleinfo ta LEFT JOIN user_info tb ON ta.uid = tb.id " +"WHERE ta.id = #{id}")ArticleInfo queryUserByUid(Integer id);
}
#{} 和 ${}的区别
从上面两个例子可以看出:
- #{} 使用的是预编译 SQL,通过 ? 占位的方式,提前对 SQL 进行编译,然后把参数填中. #{} 会根据参数类型,自动拼接引号 ‘’ .(自动添加)
- ${} 会直接进行字符替换,一起对 SQL 进行编译。如果参数为字符串,需要加上引号 ‘’(手动添加)
- 参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降
#{}
和${}
的区别在于:#{}
采用预编译SQL的方式,能防止SQL注入,更安全,会把参数用占位符?
替代后提前编译SQL;${}
是即时SQL,直接进行字符替换后再编译SQL,存在SQL注入风险,且可能因参数格式等问题影响性能或索引使用
排序功能
-从上面的例子中,可以得出结论:${}
会有SQL注入的风险,所以我们尽量使用#{}
完成查询
-既然如此,是不是${}
就没有存在的必要性了呢?当然不是.
接下来我们看下${}
的使用场景:
- Mapper实现
@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,就会加上引号. -
除此之外,还有表名作为参数时,也只能使用
${}
like查询
在 MyBatis 中使用 LIKE
进行模糊查询时,#{}
和 ${}
的使用方式及区别如下:
- 使用
#{}
实现 LIKE 查询(推荐)
#{}
是预编译方式,会自动处理参数,避免 SQL 注入,是更安全的方式。
需要通过字符串拼接通配符(%
),有两种实现方式:
方式1:在 Java 代码中拼接通配符
// 调用时在参数中加入 %
String username = "张";
List<User> users = userMapper.selectByUsername("%" + username + "%");
<!-- Mapper XML -->
<select id="selectByUsername" resultType="User">SELECT * FROM user WHERE username LIKE #{username}
</select>
- 在 SQL 中使用
CONCAT()
拼接
<!-- Mapper XML 中直接拼接 % -->
<select id="selectByUsername" resultType="User">SELECT * FROM user WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
- 使用
${}
实现 LIKE 查询(不推荐,有注入风险)
${}
是直接字符串替换,会原样插入参数,可能导致 SQL 注入,仅在确认参数安全时使用。
<select id="selectByUsername" resultType="User">SELECT * FROM user WHERE username LIKE '%${username}%'
</select>
风险示例:
若 username
参数为 ' OR '1'='1
,替换后 SQL 会变成:
SELECT * FROM user WHERE username LIKE '%' OR '1'='1%'
,导致查询所有数据,引发安全问题
数据库连接
上面MyBatis的讲解中,我们使用了数据库连接池的技术,避免频繁创建,销毁连接
介绍
数据库连接池分配负责,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是需要再建立一个
- 没有使用数据库连接池的情况:每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源。这种重复的创建连接,销毁连接比较消耗资源
- 使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库连接池中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池
优点:
- 减少网络开销
- 资源重用
- 提升系统性能
使用
常见的数据库连接池:
- C3P0
- DBCP
- Druid
- Hikari
目前比较流行的是 Hikari、Druid
1. Hikari
SpringBoot默认使用的数据库连接池
2023-08-18 18:59:51.834 INFO 54668 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-08-18 18:59:51.974 INFO 54668 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
[UserInfo(id=null, username=admin, age=18, gender=1, phone=18612340801, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=zhangsan, age=18, gender=1, phone=18612340802, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=lisi, age=18, gender=1, phone=18612340803, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=wangwu, age=18, gender=1, phone=18612340804, status=null, createTime=null, updateTime=null)]
Hikari 是日语"光"的意思(ひかり), Hikari也是以追求性能极致为目标
2. Druid
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要引入相关依赖即可
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency>
如果SpringBoot版本为2.X, 使用druid-spring-boot-starter
依赖
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>
运行结果:
2023-08-19 11:37:15.733 INFO 64692 --- [ main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2023-08-19 11:37:16.029 INFO 64692 --- [ main] com.alibaba.druid.pool.DruidDataSource : dataSource-1 inited
2023-08-19 11:37:16.361 INFO 64692 --- [ main] d.SpringbootMybatisDemoApplicationTests : Started SpringbootMybatisDemoApplicationTests in 2.381 seconds (JVM running for 3.359)
[UserInfo(id=null, username=admin, age=18, gender=1, phone=18612340801, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=zhangsan, age=18, gender=1, phone=18612340802, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=lisi, age=18, gender=1, phone=18612340803, status=null, createTime=null, updateTime=null), UserInfo(id=null, username=wangwu, age=18, gender=1, phone=18612340804, status=null, createTime=null, updateTime=null)]
2023-08-19 11:37:16.741 INFO 64692 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : dataSource-1 closing ...
2023-08-19 11:37:16.760 INFO 64692 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : dataSource-1 closed
参考官方地址: https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一