MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
在学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。
在客户端工具中,编写增删改查的SQL语句,发给MySQL数据库管理系统,由数据库管理系统执行SQL语句并返回执行结果。
增删改操作:返回受影响行数
查询操作:返回结果集(查询的结果)
做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库,现在主流的方式是:Mybatis。
虽然说通过Mybatis可以很方便的进行数据库的访问操作。但是大家要明白,其实java语言操作数据库呢,只能通过一种方式:使用sun公司提供的 JDBC 规范。而Mybatis框架,就是对原始的JDBC程序的封装。
JDBC: ( Java DataBase Connectivity ),就是使用Java语言操作关系型数据库的一套API。用于连接和执行查询以访问不同类型的数据库。JDBC 提供了一个标准库,用于建立数据库连接、执行 SQL 语句以及处理查询结果。它作为 Java 应用程序和各种关系数据库之间的桥梁,使得数据库操作可以在 Java 中以一种平台无关的方式进行。
以下是使用 JDBC 连接 MySQL 数据库的 Java 示例代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
try {
// 第一步:加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 第二步:建立数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testDB", "用户名", "密码");
// 第三步:创建 Statement 对象
Statement stmt = conn.createStatement();
// 第四步:执行 SQL 查询
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
// 第五步:处理查询结果
while (rs.next()) {
System.out.println(rs.getString("name") + ", " + rs.getInt("age"));
}
// 第六步:关闭资源
rs.close();
stmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
DriverManager(类):数据库驱动管理类。
- 作用:
- 注册驱动
- 创建java代码和数据库之间的连接,即获取Connection对象
Connection(接口):建立数据库连接的对象
- 作用:用于建立java程序和数据库之间的连接
Statement(接口): 数据库操作对象(执行SQL语句的对象)。
- 作用:用于向数据库发送sql语句
ResultSet(接口):结果集对象(一张虚拟表)
- 作用:sql查询语句的执行结果会封装在ResultSet中
通过上述代码,我们看到直接基于JDBC程序来操作数据库,代码实现非常繁琐,所以在项目开发中,我们很少使用。 在项目开发中,通常会使用Mybatis这类的高级技术来操作数据库,从而简化数据库操作、提高开发效率。
原始的JDBC程序,存在以下几点问题:
分析在mybatis中,是如何解决这些问题的:
而对于Mybatis来说,我们在开发持久层程序操作数据库时,需要重点关注以下两个方面:
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234
@Mapper
public interface UserMapper {
@Select("select id, name, age, gender, phone from user")
public List<User> list();
}
数据库连接池是个容器,负责分配、管理数据库连接(Connection)
没有使用数据库连接池:
- 客户端执行SQL语句:要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行后又需要关闭连接对象从而释放资源,每次执行SQL时都需要创建连接、销毁链接,这种频繁的重复创建销毁的过程是比较耗费计算机的性能。
- 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象
允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个- 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
- 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class HikariCPExample {
public static void main(String[] args) {
// 配置 HikariCP
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/testDB");
config.setUsername("用户名");
config.setPassword("密码");
// 创建连接池
HikariDataSource ds = new HikariDataSource(config);
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 从连接池中获取连接
conn = ds.getConnection();
// 创建 Statement 对象
stmt = conn.createStatement();
// 执行 SQL 查询
rs = stmt.executeQuery("SELECT * FROM employees");
// 处理查询结果
while (rs.next()) {
System.out.println(rs.getString("name") + ", " + rs.getInt("age"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 关闭连接池
ds.close();
}
}
Lombok 是一个 Java 库,旨在通过注解(Annotations)减少 Java 代码的样板代码(Boilerplate Code)。Lombok 可以帮助开发者自动生成诸如 getter、setter、equals、hashCode 和 toString 方法等常用代码。这样不仅减少了代码量,还提高了代码的可读性和维护性。
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法 |
第1步:在pom.xml文件中引入依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
第2步:在实体类上添加注解
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
}
在实体类上添加了@Data注解,那么这个类在编译时期,就会生成getter/setter、equals、hashcode、toString等方法。
说明:@Data注解中不包含全参构造方法,通常在实体类上,还会添加上:全参构造、无参构造
import lombok.Data;
@Data //getter方法、setter方法、toString方法、hashCode方法、equals方法
@NoArgsConstructor //无参构造
@AllArgsConstructor//全参构造
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
}
Lombok的注意事项:
MyBatis 是一个开源的 SQL 映射框架,它允许你直接使用 SQL 语句,而不需要像 JPA 那样使用 HQL 或 JPQL。MyBatis 可以与 Java 对象模型 (POJOs) 直接映射 SQL 数据库记录,为开发者提供更多的灵活性。
查询是数据库交互中非常常见的一种操作。在 MyBatis 中,我们可以通过多种方式来进行查询。
XML 配置
在 XML 映射文件中,你可以像下面这样定义一个根据 ID 查询的 SQL 语句:
<select id="getEmployeeById" parameterType="int" resultType="com.example.Employee">
SELECT * FROM employee WHERE id = #{id}
select>
Java 代码
在 Java 代码中,你可以这样使用:
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployeeById(1);
System.out.println(employee);
}
XML 配置
条件查询通常更复杂,但也可以通过 MyBatis 的 XML 配置来实现:
<select id="findEmployees" resultType="com.example.Employee">
SELECT * FROM employee
<where>
<if test="name != null">
name = #{name}
if>
<if test="age != null">
AND age = #{age}
if>
where>
select>
Java 代码
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.findEmployees("John", 30);
for (Employee employee : employees) {
System.out.println(employee);
}
}
XML 配置
<insert id="insertEmployee" parameterType="com.example.Employee">
INSERT INTO employee (name, age) VALUES (#{name}, #{age})
insert>
Java 代码
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee newEmployee = new Employee("John", 30);
mapper.insertEmployee(newEmployee);
session.commit();
}
XML 配置
<update id="updateEmployee" parameterType="com.example.Employee">
UPDATE employee SET name = #{name}, age = #{age} WHERE id = #{id}
update>
Java 代码
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee existingEmployee = new Employee(1, "Jane", 25);
mapper.updateEmployee(existingEmployee);
session.commit();
}
删除操作通常需要小心进行,以避免误删数据。
XML 配置
<delete id="deleteById" parameterType="int">
DELETE FROM employee WHERE id = #{id}
delete>
Java 代码
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
mapper.deleteById(1);
session.commit();
}
XML 配置
<delete id="deleteByIds" parameterType="list">
DELETE FROM employee WHERE id IN
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
foreach>
delete>
Java 代码
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Integer> ids = Arrays.asList(1, 2, 3);
mapper.deleteByIds(ids);
session.commit();
}
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。
在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
- resultType属性,指的是查询返回的单条记录所封装的类型。
第2步:编写XML映射文件
xml映射文件中的dtd约束,直接从mybatis官网复制即可
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
mapper>
配置:XML映射文件的namespace属性为Mapper接口全限定名
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
mapper>
配置:XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
select>
mapper>
MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。
可以通过MybatisX快速定位:
动态 SQL 是 MyBatis 非常强大的一个特性,它允许你在 SQL 语句中使用动态元素,从而实现更灵活的 SQL 查询。
在 MyBatis 中,if 元素是用于构造条件 SQL 语句的一种常用动态 SQL 元素。通过使用 if 元素,你可以根据特定条件来动态地添加或修改 SQL 语句,这样可以使得你的 SQL 语句更加灵活和可维护。
if 元素允许你根据条件来决定是否需要添加某个 SQL 片段。
XML 配置示例
以下面这个用于搜索员工的 SQL 语句为例:
<select id="findEmployees" resultType="com.example.Employee">
SELECT * FROM employee
<where>
<if test="name != null">
AND name = #{name}
if>
<if test="age != null">
AND age = #{age}
if>
where>
select>
Java 代码示例
Map<String, Object> params = new HashMap<>();
params.put("name", "John");
params.put("age", null);
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.findEmployees(params);
for (Employee employee : employees) {
System.out.println(employee);
}
}
在这个例子中,如果 name 和 age 都不为 null,则生成的 SQL 语句会同时包括这两个条件。如果其中任何一个为 null,对应的条件则不会被包括在内。
也可以将多个 if 元素组合在一起,以构造更复杂的动态 SQL。
XML 配置示例
<select id="findEmployees" resultType="com.example.Employee">
SELECT * FROM employee
<where>
<if test="name != null and name != ''">
AND name = #{name}
if>
<if test="age != null and age >= 18">
AND age = #{age}
if>
<if test="department != null">
AND department = #{department}
if>
where>
select>
这里,我们添加了更多的 if 条件,并且每个条件都更加详细。例如,对于 age,除了判断是否为 null,还额外判断了其是否大于或等于 18。
在 MyBatis 中,foreach 是一个非常有用的动态 SQL 元素,用于迭代一个集合,并在每一次迭代中都执行某种操作。这个元素特别适用于执行 IN 查询或批量操作。
foreach 元素主要用于生成 SQL 语句中的 IN 子句。
XML 配置示例
假设我们有一个需求:根据一组员工 ID 查找所有符合条件的员工记录。
<select id="findEmployeesByIds" resultType="com.example.Employee">
SELECT * FROM employee WHERE id IN
<foreach item="id" index="index" collection="ids" open="(" separator="," close=")">
#{id}
foreach>
select>
在这个例子中,foreach 元素会遍历名为 ids 的集合,并将集合中的每一个元素插入到 SQL 语句中。
Java 代码示例
List<Integer> employeeIds = Arrays.asList(1, 2, 3);
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.findEmployeesByIds(employeeIds);
for (Employee employee : employees) {
System.out.println(employee);
}
}
foreach 元素不仅可以用于 IN 查询,还可以用于批量的 INSERT、UPDATE 或 DELETE 操作。
批量插入
<insert id="insertEmployees">
INSERT INTO employee (name, age) VALUES
<foreach item="employee" index="index" collection="employeeList" separator=",">
(#{employee.name}, #{employee.age})
foreach>
insert>
Java 代码示例
List<Employee> newEmployees = Arrays.asList(
new Employee("John", 25),
new Employee("Jane", 30),
new Employee("Doe", 35)
);
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
mapper.insertEmployees(newEmployees);
session.commit();
}
在 MyBatis 中,sql 和 include 元素用于复用 SQL 代码片段,这样可以减少冗余代码并提高可维护性。通过定义一个可复用的 sql 元素并在需要的地方使用 include 元素来插入这个 SQL 片段,你可以让你的 MyBatis 映射文件更加整洁。
sql 元素用于定义一个可复用的 SQL 代码片段。
XML 配置示例
<sql id="employeeColumns">
id, name, age, department
sql>
在这个例子中,我们定义了一个名为 employeeColumns 的 sql 元素,这个元素包含了 employee 表中的列名。
include 元素用于插入一个已定义的 sql 元素。
XML 配置示例
<select id="findEmployees" resultType="com.example.Employee">
SELECT
<include refid="employeeColumns"/>
FROM employee
select>
在这个例子中,include 元素通过 refid 属性引用了前面定义的 employeeColumns SQL 片段。
Java 代码示例
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.findEmployees();
for (Employee employee : employees) {
System.out.println(employee);
}
}
你还可以在 sql 元素中使用动态 SQL。
XML 配置示例
<sql id="dynamicWhere">
<where>
<if test="name != null">
name = #{name}
if>
<if test="age != null">
AND age = #{age}
if>
where>
sql>
<select id="findEmployeesByCondition" resultType="com.example.Employee">
SELECT
<include refid="employeeColumns"/>
FROM employee
<include refid="dynamicWhere"/>
select>
这样,你可以在多个查询中复用相同的 WHERE 子句,而不需要每次都重新编写这些逻辑。