Spring Boot- 2 (数万字入门教程 ):数据交互篇
JDBC交互框架:
Spring的JDBC操作工具:
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
JDBC的模版类:JdbcTemplate
引入Mysql的依赖
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
application的配置信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: 你的用户名
password: 你的密码
driver-class-name: com.mysql.cj.jdbc.Driver
使用JdbcTemplate来操作
Yml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
我们要操作数据库,最简单直接的方法就是使用JdbcTemplate来完成:
@Resource
JdbcTemplate template;
实例:
@Test
void contextLoads() {
Map<String, Object> map = template.queryForMap(
"select * from user where id = ?", 1
);
System.out.println(map);
}
亦可以查询类:
@Data@AllArgsConstructorpublic class User {
int id;
String name;
String email;
String password;
}
@Testvoid contextLoads() {
User user = template.queryForObject("select * from user where id = ?",
(r, i) -> new User(r.getInt(1), r.getString(2), r.getString(3), r.getString(4)), 1);
System.out.println(user);
}
第一个参数:SQL 查询语句,使用?作为占位符。
第二个参数:RowMapper 接口的实现,用于将查询结果映射到 User 对象。这里使用了 Lambda 表达式:
r.getInt(1):获取结果集中第一列(id)。
r.getString(2):获取结果集中第二列(name)。
r.getString(3):获取结果集中第三列(email)。
r.getString(4):获取结果集中第四列(password)。
第三个参数:SQL 查询参数,用于替换?占位符。
这个测试方法的功能是从数据库中查询 id 为 1 的用户,并将结果打印输出。
亦可以进行查询,更新,删除,使用update
@Repositorypublic class UserDao {
private final JdbcTemplate template;
public UserDao(JdbcTemplate template) {
this.template = template;
}
// 插入用户
public int insertUser(User user) {
String sql = "INSERT INTO user (id, name, email, password) VALUES (?, ?, ?, ?)";
return template.update(sql,
user.getId(),
user.getName(),
user.getEmail(),
user.getPassword());
}
// 更新用户
public int updateUser(User user) {
String sql = "UPDATE user SET name = ?, email = ?, password = ? WHERE id = ?";
return template.update(sql,
user.getName(),
user.getEmail(),
user.getPassword(),
user.getId());
}
// 删除用户
public int deleteUser(int id) {
String sql = "DELETE FROM user WHERE id = ?";
return template.update(sql, id);
}
// 查询用户(保留原示例)
public User getUserById(int id) {
String sql = "SELECT * FROM user WHERE id = ?";
return template.queryForObject(sql, userRowMapper(), id);
}
// RowMapper 提取为独立方法提高复用性
private RowMapper<User> userRowMapper() {
return (rs, rowNum) -> new User(
rs.getInt("id"),
rs.getString("name"),
rs.getString("email"),
rs.getString("password")
);
}
}
如果是小项目or测试方法,JdbcTemplate可以很快的辅助我们完成操作
Jdbc的简单封装:
SimpleJdbcInsert来处理一些高级的插入:SimpleJdbcInsert 是 Spring 框架提供的一个便捷工具类,用于简化 JDBC 插入操作,尤其适合处理动态插入场景。与直接使用 JdbcTemplate 相比,它提供了更高级的特性,如自动生成主键、基于数据库元数据的表结构感知等。
核心特性
1,自动表结构感知:通过数据库元数据自动获取表结构信息
2,主键生成支持:支持自增主键和序列(如 Oracle)
3,参数类型自动匹配:基于列类型自动转换参数
4,批处理支持:高效处理批量插入操作
5,简化的 API:链式调用风格,代码更简洁
看一看这个的基本使用方法:
@Repositorypublic class AdvancedUserDao {
private final SimpleJdbcInsert jdbcInsert;
public AdvancedUserDao(DataSource dataSource) {
// 初始化 SimpleJdbcInsert,指定数据源和表名
this.jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("user")
.usingGeneratedKeyColumns("id"); // 指定自增主键列
}
// 插入用户并返回生成的主键
public Number insertUser(User user) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", user.getName());
parameters.put("email", user.getEmail());
parameters.put("password", user.getPassword());
// 执行插入并返回自动生成的主键
return jdbcInsert.executeAndReturnKey(parameters);
}
public void batchInsertUsers(List<User> users) {
// 1. 创建一个用于存储批量插入参数的列表
List<Map<String, Object>> batch = new ArrayList<>();
// 2. 遍历用户列表,为每个用户创建参数映射
for (User user : users) {
// 3. 为当前用户创建一个键值对映射,键为列名,值为列值
Map<String, Object> parameters = new HashMap<>();
// 4. 添加用户名列
parameters.put("name", user.getName());
// 5. 添加邮箱列
parameters.put("email", user.getEmail());
// 6. 添加密码列
parameters.put("password", user.getPassword());
// 7. 将当前用户的参数映射添加到批量参数列表中
batch.add(parameters);
}
// 8. 执行批量插入操作
// batch.toArray(new Map[0]) 将列表转换为 Map 数组
// executeBatch 方法会将数组中的每个 Map 作为一组参数执行插入
jdbcInsert.executeBatch(batch.toArray(new Map[0]));
}
}
还可以自定义列映射:
public AdvancedUserDao(DataSource dataSource) {
this.jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("user")
.usingColumns("name", "email", "password") // 显式指定要插入的列
.withoutTableColumnMetaDataAccess(); // 禁用元数据访问(提高性能)
}
处理复合主键:
public Number[] insertUserWithCompositeKey(User user) {
Map<String, Object> params = new HashMap<>();
params.put("org_id", user.getOrgId()); // 复合主键字段1
params.put("user_id", user.getUserId()); // 复合主键字段2
params.put("name", user.getName());
// 返回包含所有主键值的 Map
return jdbcInsert.executeAndReturnKeyHolder(params).getKeys();
}
上面的都是一些小型的工具,帮助我们实现一个小项目or测试方法的时候可以用,更复杂的就不建议了
JPA框架:
Spring Data JPA 是 Spring 框架的一部分,旨在简化基于 JPA(Java Persistence API)的数据访问层开发。它通过提供统一的接口和自动实现机制,大幅减少了数据访问层的样板代码,让开发者可以更专注于业务逻辑。
核心思想,将实体类对应一个数据库表
first,引入依赖:
同样的,我们只需要导入stater依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
second,创建对应的类:
@Data
public class Account {
int id;
String username;
String password;
}
third添加实体类和数据库表的映射关系:
@Data
@Entity //表示这个类是一个实体类
@Table(name = "account") //对应的数据库中表名称
public class Account {
@GeneratedValue(strategy = GenerationType.IDENTITY) //生成策略,这里配置为自增 @Column(name = "id") //对应表中id这一列
@Id //此属性为主键
int id;
@Column(name = "username") //对应表中
username这一列 String username;
@Column(name = "password") //对应表中password这一列
String password;
}
Fourth:配置yml文件
spring:
jpa:
#开启SQL语句执行日志信息
show-sql: true
hibernate:
#配置为检查数据库表结构,没有时会自动创建
ddl-auto: update
ddl-auto属性用于设置自动表定义,可以实现自动在数据库中为我们创建一个表,表的结构会根据我们定义的实体类决定,它有以下几种:
none: 不执行任何操作,数据库表结构需要手动创建。
create: 框架在每次运行时都会删除所有表,并重新创建。
create-drop: 框架在每次运行时都会删除所有表,然后再创建,但在程序结束时会再次删除所有表。
update: 框架会检查数据库表结构,如果与实体类定义不匹配,则会做相应的修改,以保持它们的一致性。
validate: 框架会检查数据库表结构与实体类定义是否匹配,如果不匹配,则会抛出异常。
这个配置项的作用是为了避免手动管理数据库表结构,使开发者可以更方便地进行开发和测试,但在生产环境中,更推荐使用数据库迁移工具来管理表结构的变更。
fifth:访问表
一个repository类,继承接口JpaRepository<>:
@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> { }
JpaRepository<实体类, ID类型>
注入这个Repository类进行查询就好啦
@Resource
AccountRepository repository;
@Test
void contextLoads() {
Account account = new Account();
account.setUsername("小红");
account.setPassword("1234567");
System.out.println(repository.save(account).getId()); //使用save来快速插入数据,并且会返回插入的对象,如果存在自增ID,对象的自增id属性会自动被赋值,这就很方便了
}
查询就可以使用 findxx():
@Test
void contextLoads() {
//默认通过通过ID查找的方法,并且返回的结果是Optional包装的对象,非常人性化
repository.findById(1).ifPresent(System.out::println);
}
方法名拼接自定义SQL
属性 | 拼接方法名称示例 | 执行的语句 |
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull,Null | findByAge(Is)Null | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1(参数与附加%绑定) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1(参数与前缀%绑定) |
Containing | findByFirstnameContaining | … where x.firstname like ?1(参数绑定以%包装) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue | … where x.active = true |
False | findByActiveFalse | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
关联查询:
一对一关联:
因为我们JPA的核心思想史对象对应一个表,所以关联就可以看做一个对象和对象关联
比如:用户信息和用户详细信息的一对一关联
的依赖关系,比如用户表中包含了用户详细信息的ID字段作为外键,那么实际上就是用户表实体中包括了用户详细信息实体对象:
@Data
@Entity
@Table(name = "users_detail")
public class AccountDetail {
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
int id;
@Column(name = "address")
String address;
@Column(name = "email")
String email;
@Column(name = "phone")
String phone;
@Column(name = "real_name")
String realName;
}
@Data
@Entity
@Table(name = "users")
public class Account {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@Id
int id;
@Column(name = "username")
String username;
@Column(name = "password")
String password;
@JoinColumn(name = "detail_id") //指定存储外键的字段名称
@OneToOne //声明为一对一关系
AccountDetail detail;
}
还有其他的一对多,多对一,多对多的关系
如果只需要查询用户信息,不需要详细信息就可以设置一个懒加载,这样就不会每次查询都去找详细信息了,只会在需要的时候才会去查
@OneToOne(fetch = FetchType.LAZY) //将获取类型改为LAZY
当然了,如果你想再加入一个表的数据的时候对关联的表也去操作,就可以使用cascade
ALL:所有操作都进行关联操作
PERSIST:插入操作时才进行关联操作
REMOVE:删除操作时才进行关联操作
MERGE:修改操作时才进行关联操作
就变这样了:
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) //设置关联操作为ALL AccountDetail detail;
一对多:
@oneTomany:
@manyToone:
比如一个成绩排名对应了很多的学生,学生们都对应的同一个成绩榜单,多对一,这里的学生是多:
@Entitypublic class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// 多对一:多个学生关联同一个成绩单
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "transcript_id") // 外键列,指向成绩单ID
private Transcript transcript;
// 构造方法、Getter和Setter
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Transcript getTranscript() { return transcript; }
public void setTranscript(Transcript transcript) { this.transcript = transcript; }
}
@Entitypublic class Transcript {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String course; // 课程名称
private double score; // 分数(共享的统一分数)
// 一对多:一个成绩单对应多个学生
@OneToMany(mappedBy = "transcript", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Student> students = new ArrayList<>();
// 构造方法、Getter和Setter
public Transcript() {}
public Transcript(String course, double score) {
this.course = course;
this.score = score;
}
// 添加学生的便捷方法
public void addStudent(Student student) {
students.add(student);
student.setTranscript(this);
}
// 移除学生的便捷方法
public void removeStudent(Student student) {
students.remove(student);
student.setTranscript(null);
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getCourse() { return course; }
public void setCourse(String course) { this.course = course; }
public double getScore() { return score; }
public void setScore(double score) { this.score = score; }
public List<Student> getStudents() { return students; }
public void setStudents(List<Student> students) { this.students = students; }
}
多对多:
@ManyToMany:
学生(Student)与课程(Course) 的关系。一个学生可以选修多门课程,一门课程也可以被多个学生选修。
一、实体类设计
1. 学生实体(Student)
@Entity
@Table(name = "students")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// 多对多关系:学生选修多门课程
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "student_course", // 中间表名
joinColumns = @JoinColumn(name = "student_id"), // 指向当前实体(学生)的外键
inverseJoinColumns = @JoinColumn(name = "course_id") // 指向关联实体(课程)的外键
)
private List<Course> courses = new ArrayList<>();
// 构造方法、Getter和Setter
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 添加课程的便捷方法
public void addCourse(Course course) {
courses.add(course);
course.getStudents().add(this);
}
// 移除课程的便捷方法
public void removeCourse(Course course) {
courses.remove(course);
course.getStudents().remove(this);
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public List<Course> getCourses() { return courses; }
public void setCourses(List<Course> courses) { this.courses = courses; }
}
2. 课程实体(Course)
@Entity
@Table(name = "courses")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String courseName;
private int credits;
// 多对多关系:课程被多个学生选修
@ManyToMany(mappedBy = "courses") // 映射到 Student 实体的 courses 字段
private List<Student> students = new ArrayList<>();
// 构造方法、Getter和Setter
public Course() {}
public Course(String courseName, int credits) {
this.courseName = courseName;
this.credits = credits;
}
// 添加学生的便捷方法
public void addStudent(Student student) {
students.add(student);
student.getCourses().add(this);
}
// 移除学生的便捷方法
public void removeStudent(Student student) {
students.remove(student);
student.getCourses().remove(this);
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getCourseName() { return courseName; }
public void setCourseName(String courseName) { this.courseName = courseName; }
public int getCredits() { return credits; }
public void setCredits(int credits) { this.credits = credits; }
public List<Student> getStudents() { return students; }
public void setStudents(List<Student> students) { this.students = students; }
}
二、数据库表结构
多对多关系通过 中间表(Join Table) 实现:
1. students 表
字段名 | 类型 | 描述 |
id | BIGINT | 主键 |
name | VARCHAR | 学生姓名 |
age | INT | 学生年龄 |
2. courses 表
字段名 | 类型 | 描述 |
id | BIGINT | 主键 |
course_name | VARCHAR | 课程名称 |
credits | INT | 学分 |
3. student_course 中间表
字段名 | 类型 | 描述 |
student_id | BIGINT | 外键,关联 students 表 |
course_id | BIGINT | 外键,关联 courses 表 |
PRIMARY KEY | (student_id, course_id) | 复合主键 |
三.Repository 接口
public interface StudentRepository extends JpaRepository<Student, Long> {}
public interface CourseRepository extends JpaRepository<Course, Long> {}
四、业务逻辑示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StudentCourseService {
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
// 创建学生并分配课程
@Transactional
public Student createStudentWithCourses(String name, int age, List<Course> courses) {
Student student = new Student(name, age);
courses.forEach(course -> student.addCourse(course));
return studentRepository.save(student);
}
// 创建课程并分配学生
@Transactional
public Course createCourseWithStudents(String courseName, int credits, List<Student> students) {
Course course = new Course(courseName, credits);
students.forEach(student -> course.addStudent(student));
return courseRepository.save(course);
}
// 为学生添加课程
@Transactional
public void addCourseToStudent(Long studentId, Long courseId) {
Student student = studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalArgumentException("Student not found"));
Course course = courseRepository.findById(courseId)
.orElseThrow(() -> new IllegalArgumentException("Course not found"));
student.addCourse(course);
}
// 获取学生及其选修的课程
@Transactional(readOnly = true)
public Student getStudentWithCourses(Long studentId) {
return studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalArgumentException("Student not found"));
}
// 删除学生及其选课记录
@Transactional
public void deleteStudent(Long studentId) {
studentRepository.deleteById(studentId);
}
}
有时候过于复杂的语句还是只有使用SQl语句,为了不让我们再次去使用Mybatis框架,可以使用@Query(原生sql语句进行查询)
@Query("update xxx set xxx = ?2 where xxx = ?1")
int updatexxxbyxxx(第一个参数,第二个参数)
这里的?后面的数字表示填入第几个参数
MybatisPlus框架:
一、MyBatis-Plus 框架全解析
1. 基本信息
MyBatis-Plus (简称 MP) 是一个 MyBatis 的增强工具,旨在简化开发过程,提供零配置、CRUD 自动化和强大的条件构造器,使开发者能够更高效地使用 MyBatis。
核心优势:
无需编写 XML 或大量 SQL 语句
提供丰富的条件构造器,替代复杂 SQL
支持自动填充、逻辑删除、乐观锁等高级特性
内置分页插件,简化分页操作
代码生成器快速生成基础代码
二、引入依赖
Maven 依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
三、配置 application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306
username: root
password: yourpassword
mybatis-plus:
mapper-locations: classpath:mapper/*.xml # mapper XML 文件位置
global-config:
db-config:
id-type: auto # 主键类型
logic-delete-field: deleted # 逻辑删除字段名
logic-not-delete-value: 0 # 未删除值
logic-delete-value: 1 # 已删除值
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL 日志打印
map-underscore-to-camel-case: true # 下划线转驼峰
四、示例实体类定义
@Data
@TableName("user") // 对应数据库表名
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
@TableFeild(“对应的字段”)
private String username;
@TableFeild(“对应的字段”)
private String email;
@TableFeild(“对应的字段”)
private Integer age;
@TableField(对应的字段,fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(对应的字段,fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
private LocalDateTime updateTime;
@TableLogic // 逻辑删除字段
private Integer deleted;
@Version // 乐观锁版本号
private Integer version;
}
五、Mapper 接口定义
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,自动拥有 CRUD 方法
// 可添加自定义方法
}
六、简单测试方法
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectById() {
User user = userMapper.selectById(1L);
System.out.println(user);
}
}
七、条件构造器处理复杂查询
@SpringBootTest
public class QueryWrapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testComplexQuery() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.like("username", "张") // 用户名包含"张"
.ge("age", 20) // 年龄大于等于20
.le("age", 30) // 年龄小于等于30
.orderByDesc("create_time") // 按创建时间降序
.last("LIMIT 10"); // 追加SQL片段
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}
八、批处理操作
@SpringBootTest
public class BatchOperationTest {
@Autowired
private UserMapper userMapper;
@Test
@Transactional // 批量操作建议在事务中执行
public void testBatchInsert() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
User user = new User();
user.setUsername("test" + i);
user.setEmail("test" + i + "@example.com");
user.setAge(20 + i % 10);
userList.add(user);
}
// 批量插入
userList.forEach(userMapper::insert);
}
}
九、分页查询
先配置:
@Configuration
public class MybatisConfiguration {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分页拦截器到MybatisPlusInterceptor中
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor; }
}
@SpringBootTest
public class PaginationTest {
@Autowired
private UserMapper userMapper;
@Test
public void testPagination() {
// 创建分页对象,查询第1页,每页10条
Page<User> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);
// 执行分页查询
Page<User> result = userMapper.selectPage(page, wrapper);
System.out.println("总记录数: " + result.getTotal());
System.out.println("总页数: " + result.getPages());
System.out.println("当前页数据: " + result.getRecords());
}
}
Page.of(1,2)
一共2页,查看第一页
十、增删改操作
1. 新增数据
User user = new User();
user.setUsername("doubao");
user.setEmail("doubao@example.com");
user.setAge(25);
int rows = userMapper.insert(user); // 返回影响行数
System.out.println("新增用户ID: " + user.getId());
2. 删除数据
// 物理删除
int rows = userMapper.deleteById(1L);
// 逻辑删除(更新deleted字段)
int logicRows = userMapper.deleteById(2L);
3. 更新数据
User user = new User();
user.setId(1L);
user.setAge(26); // 只更新age字段
int rows = userMapper.updateById(user);
也可以使用UpdateWrapper<>来处理
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set(xxx,xxx).eq(xxx,xx)
mapper.update(null,wrapper)
十一、IService 接口的继承与实现
1. 定义 Service 接口
public interface UserService extends IService<User> {
// 继承 IService 后,自动拥有基础 CRUD 方法
// 自定义方法
List<User> findByAgeGreaterThan(Integer age);}
2. 实现 Service 接口
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> findByAgeGreaterThan(Integer age) {
return baseMapper.selectList(new QueryWrapper<User>().gt("age", age));
}
}
3. 使用示例
@Autowired
private UserService userService;
@Test
public void testCustomServiceMethod() {
List<User> users = userService.findByAgeGreaterThan(20);
users.forEach(System.out::println);
}
MyBatis-Plus 通过 BaseMapper 和 IService 接口提供了强大的 CRUD 能力,同时通过 条件构造器 简化了复杂查询。开发者可以在继承基础接口的同时,添加自定义方法,实现灵活扩展。
关键组件:
BaseMapper:提供基础 CRUD 方法
IService:提供更高级的业务层方法(如批量操作)
ServiceImpl:默认实现类,集成 BaseMapper
Wrapper:强大的条件构造器,替代复杂 SQL
通过这种方式,MyBatis-Plus 大幅减少了样板代码,提高了开发效率,同时保留了 MyBatis 的灵活性。