# Spring 数据访问层技术详解
## 第1章 数据访问层概述
在现代企业应用开发中,数据访问层是连接业务逻辑与数据库的桥梁。Spring生态系统提供了多种数据访问技术,其中最常用的包括JPA、MyBatis和MyBatis-Plus。本章将详细介绍这三种技术的核心概念、使用方法、优缺点以及适用场景。
## 第2章 JPA(Java Persistence API)
### 2.1 JPA概述
JPA是Java持久化API(Java Persistence API)的缩写,是Java EE的一部分,为Java开发者提供了一种对象关系映射(ORM)的标准规范。Spring Data JPA是Spring对JPA的封装,简化了JPA的使用,提供了Repository层的实现。
### 2.2 JPA核心概念
1. **实体(Entity)**:映射到数据库表的Java类
2. **实体管理器(EntityManager)**:负责实体的CRUD操作
3. **持久化上下文(Persistence Context)**:管理实体的生命周期
4. **JPQL(Java Persistence Query Language)**:面向对象的查询语言
5. **Repository接口**:Spring Data提供的数据访问接口
### 2.3 JPA使用示例
#### 2.3.1 实体类定义
```java
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Table(name = "user")
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", length = 50, nullable = false, unique = true)
private String username;
@Column(name = "password", length = 100, nullable = false)
private String password;
@Column(name = "email", length = 100)
private String email;
@Column(name = "created_time")
private Date createdTime;
@Column(name = "updated_time")
private Date updatedTime;
@Transient
private String tempField; // 不会映射到数据库字段
}
```
#### 2.3.2 Repository接口定义
```java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
// 方法名查询:根据用户名查询用户
Optional<User> findByUsername(String username);
// 方法名查询:根据用户名和邮箱查询用户
Optional<User> findByUsernameAndEmail(String username, String email);
// 方法名查询:查询用户名包含指定字符的用户列表
List<User> findByUsernameContaining(String keyword);
// 方法名查询:根据ID范围查询用户列表
List<User> findByIdBetween(Long startId, Long endId);
// 删除用户
void deleteByUsername(String username);
// 判断用户是否存在
boolean existsByUsername(String username);
// 统计用户数量
long countByEmailEndingWith(String domain);
// 自定义JPQL查询
@Query("SELECT u FROM User u WHERE u.username = :username AND u.password = :password")
Optional<User> findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
// 原生SQL查询
@Query(value = "SELECT * FROM user WHERE created_time > :date", nativeQuery = true)
List<User> findUsersCreatedAfter(@Param("date") Date date);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.password = :password WHERE u.id = :id")
int updatePassword(@Param("id") Long id, @Param("password") String password);
}
```
#### 2.3.3 Service层实现
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 保存用户
@Transactional
public User saveUser(User user) {
user.setCreatedTime(new Date());
user.setUpdatedTime(new Date());
return userRepository.save(user);
}
// 根据ID查询用户
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
// 根据用户名查询用户
public Optional<User> getUserByUsername(String username) {
return userRepository.findByUsername(username);
}
// 分页查询用户
public Page<User> getUsersByPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdTime"));
return userRepository.findAll(pageable);
}
// 更新用户密码
@Transactional
public int updateUserPassword(Long id, String password) {
return userRepository.updatePassword(id, password);
}
// 删除用户
@Transactional
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
// 复杂条件查询(使用Specification)
public List<User> queryUsers(String username, String email) {
return userRepository.findAll((root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(username)) {
predicates.add(criteriaBuilder.like(root.get("username"), "%" + username + "%"));
}
if (StringUtils.hasText(email)) {
predicates.add(criteriaBuilder.equal(root.get("email"), email));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
});
}
}
```
### 2.4 JPA配置
#### 2.4.1 application.yml配置
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # 可选值:none, create, create-drop, update, validate
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
open-in-view: false
```
### 2.5 JPA的优缺点
**优点**:
- 标准化的ORM规范,与具体实现无关
- 面向对象的查询语言,不需要编写SQL
- 开发效率高,代码量少
- 强大的对象关系映射能力
- 支持复杂的对象图操作
**缺点**:
- 对于复杂查询,JPQL不够灵活
- SQL优化难度较大
- 学习曲线较陡峭
- 性能开销相对较大
## 第3章 MyBatis
### 3.1 MyBatis概述
MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects)为数据库中的记录。
### 3.2 MyBatis核心概念
1. **SqlSessionFactory**:MyBatis的核心工厂类,负责创建SqlSession
2. **SqlSession**:执行SQL的主要接口
3. **Mapper接口**:定义SQL操作的接口
4. **Mapper XML文件**:配置SQL语句和结果映射
5. **TypeHandler**:处理Java类型和JDBC类型之间的转换
6. **ResultMap**:定义结果集的映射规则
### 3.3 MyBatis使用示例
#### 3.3.1 Mapper接口定义
```java
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface UserMapper {
// 注解方式配置SQL
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
@Select("SELECT * FROM user WHERE username = #{username}")
User selectByUsername(String username);
@Insert("INSERT INTO user(username, password, email, created_time, updated_time) VALUES(#{username}, #{password}, #{email}, #{createdTime}, #{updatedTime})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username = #{username}, password = #{password}, email = #{email}, updated_time = #{updatedTime} WHERE id = #{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteById(Long id);
// 动态SQL示例(注解方式)
@Select("<script>" +
"SELECT * FROM user" +
"<where>" +
"<if test='username != null and username != ""'>" +
"AND username LIKE CONCAT('%', #{username}, '%')" +
"</if>" +
"<if test='email != null and email != ""'>" +
"AND email = #{email}" +
"</if>" +
"</where>" +
"</script>")
List<User> selectByCondition(@Param("username") String username, @Param("email") String email);
// 使用XML配置的方法
List<User> selectAll();
User selectByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
List<User> selectByIdList(@Param("idList") List<Long> idList);
int batchInsert(@Param("userList") List<User> userList);
}
```
#### 3.3.2 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.example.mapper.UserMapper">
<!-- 结果映射 -->
<resultMap id="BaseResultMap" type="com.example.entity.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="email" property="email"/>
<result column="created_time" property="createdTime"/>
<result column="updated_time" property="updatedTime"/>
</resultMap>
<!-- 通用列 -->
<sql id="Base_Column_List">
id, username, password, email, created_time, updated_time
</sql>
<!-- 查询所有用户 -->
<select id="selectAll" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user
</select>
<!-- 根据用户名和密码查询用户 -->
<select id="selectByUsernameAndPassword" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user
WHERE username = #{username}
AND password = #{password}
</select>
<!-- 根据ID列表查询用户 -->
<select id="selectByIdList" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入用户 -->
<insert id="batchInsert">
INSERT INTO user(username, password, email, created_time, updated_time)
VALUES
<foreach collection="userList" item="user" separator=",">
(#{user.username}, #{user.password}, #{user.email}, #{user.createdTime}, #{user.updatedTime})
</foreach>
</insert>
<!-- 动态更新用户 -->
<update id="dynamicUpdate">
UPDATE user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="password != null and password != ''">
password = #{password},
</if>
<if test="email != null and email != ''">
email = #{email},
</if>
updated_time = #{updatedTime}
</set>
WHERE id = #{id}
</update>
<!-- 分页查询 -->
<select id="selectByPage" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
</where>
ORDER BY created_time DESC
LIMIT #{offset}, #{pageSize}
</select>
</mapper>
```
#### 3.3.3 Service层实现
```java
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 也可以直接注入SqlSession
// @Autowired
// private SqlSession sqlSession;
// 保存用户
@Transactional
public User saveUser(User user) {
user.setCreatedTime(new Date());
user.setUpdatedTime(new Date());
userMapper.insert(user);
return user;
}
// 根据ID查询用户
public User getUserById(Long id) {
return userMapper.selectById(id);
}
// 根据用户名查询用户
public User getUserByUsername(String username) {
return userMapper.selectByUsername(username);
}
// 查询所有用户
public List<User> getAllUsers() {
return userMapper.selectAll();
}
// 根据条件查询用户
public List<User> getUsersByCondition(String username, String email) {
return userMapper.selectByCondition(username, email);
}
// 批量插入用户
@Transactional
public int batchInsertUsers(List<User> userList) {
Date now = new Date();
for (User user : userList) {
user.setCreatedTime(now);
user.setUpdatedTime(now);
}
return userMapper.batchInsert(userList);
}
// 更新用户
@Transactional
public int updateUser(User user) {
user.setUpdatedTime(new Date());
return userMapper.update(user);
}
// 删除用户
@Transactional
public int deleteUser(Long id) {
return userMapper.deleteById(id);
}
}
```
### 3.4 MyBatis配置
#### 3.4.1 application.yml配置
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/**/*.xml # Mapper XML文件位置
type-aliases-package: com.example.entity # 实体类包路径
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志实现
default-fetch-size: 100 # 默认获取数量
default-statement-timeout: 30 # 默认超时时间(秒)
```
### 3.5 MyBatis的优缺点
**优点**:
- SQL语句与代码分离,便于维护
- 支持动态SQL,灵活应对各种查询场景
- 性能优秀,接近原生JDBC
- 对复杂SQL支持良好
- 学习曲线平缓,容易上手
**缺点**:
- 代码量较大,需要编写XML或注解配置
- SQL语句依赖于特定数据库,移植性较差
- 开发效率相对较低
- 不支持对象关系映射的高级特性
## 第4章 MyBatis-Plus
### 4.1 MyBatis-Plus概述
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了代码生成器、条件构造器、分页插件等功能,可以大幅减少开发工作量。
### 4.2 MyBatis-Plus核心功能
1. **CRUD接口**:内置通用Mapper、Service,无需编写XML即可完成CRUD操作
2. **条件构造器**:强大的条件构造器,支持链式调用
3. **代码生成器**:快速生成Entity、Mapper、Service、Controller等代码
4. **分页插件**:基于MyBatis的物理分页,支持多种数据库
5. **性能分析插件**:输出SQL执行耗时
6. **全局拦截插件**:提供全表CRUD拦截、批量操作等功能
7. **逻辑删除**:支持逻辑删除功能
### 4.3 MyBatis-Plus使用示例
#### 4.3.1 实体类定义
```java
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data
@TableName("user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("username")
private String username;
@TableField("password")
private String password;
@TableField("email")
private String email;
@TableField(value = "created_time", fill = FieldFill.INSERT)
private Date createdTime;
@TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
private Date updatedTime;
@TableLogic // 逻辑删除标记
@TableField("deleted")
private Integer deleted;
@TableField(exist = false) // 非数据库字段
private String tempField;
}
```
#### 4.3.2 Mapper接口定义
```java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承BaseMapper后,无需编写基本CRUD方法
// 可以添加自定义方法
// 自定义查询
@Select("SELECT * FROM user WHERE username = #{username} AND password = #{password}")
User selectByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
// 使用注解方式实现复杂查询
List<User> selectByCustomCondition(@Param("username") String username, @Param("startTime") Date startTime);
}
```
#### 4.3.3 Service接口和实现
```java
// Service接口
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
// 可以添加自定义业务方法
User findByUsername(String username);
List<User> findUsersByCondition(String username, Date startTime);
}
// Service实现类
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User findByUsername(String username) {
// 使用条件构造器
return lambdaQuery()
.eq(User::getUsername, username)
.one();
}
@Override
public List<User> findUsersByCondition(String username, Date startTime) {
// 使用条件构造器
return lambdaQuery()
.like(StringUtils.hasText(username), User::getUsername, username)
.ge(startTime != null, User::getCreatedTime, startTime)
.list();
}
// 批量插入示例
@Transactional
public boolean saveBatchUsers(List<User> userList) {
return saveBatch(userList, 100); // 第二个参数是批次大小
}
// 分页查询示例
public Page<User> pageUsers(String username, int pageNum, int pageSize) {
Page<User> page = new Page<>(pageNum, pageSize);
return lambdaQuery()
.like(StringUtils.hasText(username), User::getUsername, username)
.page(page);
}
}
```
#### 4.3.4 自定义填充处理器
```java
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时自动填充创建时间和更新时间
this.strictInsertFill(metaObject, "createdTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updatedTime", Date.class, new Date());
// 设置默认值
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时自动填充更新时间
this.strictUpdateFill(metaObject, "updatedTime", Date.class, new Date());
}
}
```
#### 4.3.5 条件构造器高级用法
```java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import java.util.Date;
import java.util.List;
@Service
public class ConditionService {
@Autowired
private UserMapper userMapper;
// QueryWrapper用法
public List<User> queryByWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("username", "admin")
.eq("status", 1)
.between("created_time", "2023-01-01", "2023-12-31")
.orderByDesc("created_time");
return userMapper.selectList(queryWrapper);
}
// LambdaQueryWrapper用法(推荐,类型安全)
public List<User> queryByLambdaWrapper(String username, Integer status) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper
.like(StringUtils.hasText(username), User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(User::getCreatedTime, new Date(2023, 0, 1))
.orderByDesc(User::getCreatedTime);
return userMapper.selectList(lambdaQueryWrapper);
}
// UpdateWrapper用法
public int updateByWrapper(Long id, String password) {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper
.set("password", password)
.set("updated_time", new Date())
.eq("id", id);
return userMapper.update(null, updateWrapper);
}
// LambdaUpdateWrapper用法
public int updateByLambdaWrapper(Long id, String email) {
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper
.set(User::getEmail, email)
.set(User::getUpdatedTime, new Date())
.eq(User::getId, id);
return userMapper.update(null, lambdaUpdateWrapper);
}
// 复杂条件查询
public List<User> complexQuery(String keyword, Date startDate, Date endDate, List<Long> ids) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper
.and(i -> i.like(User::getUsername, keyword).or().like(User::getEmail, keyword))
.between(User::getCreatedTime, startDate, endDate)
.in(User::getId, ids)
.ne(User::getStatus, -1)
.orderByAsc(User::getId);
return userMapper.selectList(wrapper);
}
}
```
### 4.4 MyBatis-Plus配置
#### 4.4.1 application.yml配置
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.example.entity
global-config:
db-config:
id-type: auto # 主键策略
logic-delete-field: deleted # 逻辑删除字段
logic-delete-value: 1 # 逻辑删除值
logic-not-delete-value: 0 # 逻辑未删除值
table-prefix: t_ # 表前缀(可选)
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
```
#### 4.4.2 配置类
```java
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {
// 配置分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
// 配置乐观锁插件(可选)
/*
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
*/
// 配置性能分析插件(仅开发环境使用)
/*
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
// SQL执行超过300ms自动停止运行,有助于发现问题
performanceInterceptor.setMaxTime(300);
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
*/
}
```
### 4.5 MyBatis-Plus的优缺点
**优点**:
- 继承了MyBatis的所有优点
- 无需编写基本的CRUD代码,提高开发效率
- 强大的条件构造器,支持链式调用
- 内置分页插件,使用简单
- 代码生成器,快速生成项目结构
- 丰富的扩展功能,如逻辑删除、字段自动填充等
**缺点**:
- 对于极其复杂的SQL,仍然需要编写XML
- 过度封装可能导致对底层MyBatis的理解不足
- 版本更新可能带来兼容性问题
## 第5章 三种ORM框架的对比与选择
### 5.1 功能对比
| 特性 | JPA | MyBatis | MyBatis-Plus |
|------|-----|---------|-------------|
| 对象关系映射 | 完整支持 | 有限支持 | 有限支持 |
| SQL控制粒度 | 低 | 高 | 中 |
| 开发效率 | 高 | 中 | 很高 |
| 学习曲线 | 陡峭 | 平缓 | 平缓 |
| 动态SQL支持 | 中等 | 强 | 强 |
| 性能 | 中等 | 高 | 高 |
| 数据库无关性 | 强 | 弱 | 弱 |
| 分页支持 | 内置 | 需要手动实现 | 内置物理分页 |
| 代码生成 | 支持 | 支持 | 强大支持 |
### 5.2 适用场景
**JPA适用场景**:
- 业务相对简单,对SQL要求不高的项目
- 追求对象模型与数据模型严格对应
- 需要跨数据库平台的项目
- 希望使用面向对象方式进行数据操作
**MyBatis适用场景**:
- 对SQL有严格控制要求的项目
- 复杂查询场景较多的项目
- 性能要求极高的项目
- 已有成熟数据库设计,需要精确映射的项目
**MyBatis-Plus适用场景**:
- 需要快速开发的项目
- 希望减少重复代码的项目
- 对MyBatis已有了解,希望增强其功能
- 需要内置分页、代码生成等功能的项目
### 5.3 混合使用策略
在实际项目中,也可以根据具体需求混合使用多种ORM框架:
1. **JPA + MyBatis**:简单查询使用JPA,复杂查询使用MyBatis
2. **MyBatis-Plus + 自定义XML**:大部分功能使用MyBatis-Plus,特殊需求使用自定义XML
3. **多模块使用不同框架**:根据不同模块的特点选择合适的框架
## 第6章 最佳实践
### 6.1 项目结构最佳实践
```
src/main/java/com/example/
├── config/ # 配置类
├── controller/ # 控制器
├── service/ # 业务层
│ └── impl/ # 业务实现
├── mapper/ # MyBatis/MyBatis-Plus映射器
├── repository/ # JPA仓库
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── vo/ # 视图对象
├── util/ # 工具类
└── exception/ # 异常处理
```
### 6.2 性能优化建议
1. **合理使用连接池**:配置适当的连接池大小
```yaml
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
```
2. **使用延迟加载**:对于关联对象,使用延迟加载减少数据量
```java
// JPA延迟加载
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
```
3. **避免N+1查询问题**:使用JOIN FETCH或显式查询
```java
// JPA避免N+1问题
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
User findUserWithRoles(@Param("id") Long id);
```
4. **合理使用缓存**:一级缓存、二级缓存和查询缓存
```yaml
# MyBatis缓存配置
mybatis:
configuration:
cache-enabled: true
```
5. **批量操作**:使用批量插入、更新等操作减少数据库交互
```java
// MyBatis-Plus批量插入
userService.saveBatch(userList, 100);
```
6. **分页查询**:使用物理分页而非内存分页
```java
// MyBatis-Plus分页
Page<User> page = new Page<>(1, 10);
Page<User> userPage = userService.page(page);
```
### 6.3 事务管理最佳实践
1. **声明式事务**:使用`@Transactional`注解管理事务
```java
@Transactional(rollbackFor = Exception.class)
public void updateUserInfo(User user) {
// 业务操作
}
```
2. **事务传播特性**:合理设置事务传播行为
```java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 方法实现
}
```
3. **事务隔离级别**:根据业务需求设置合适的隔离级别
```java
@Transactional(isolation = Isolation.READ_COMMITTED)
public void methodB() {
// 方法实现
}
```
4. **避免大事务**:事务范围尽可能小,减少锁定时间
5. **处理事务超时**:合理设置事务超时时间
```java
@Transactional(timeout = 30)
public void methodC() {
// 方法实现
}
```
### 6.4 安全最佳实践
1. **防止SQL注入**:使用参数化查询,避免字符串拼接
```java
// 安全的参数化查询
@Select("SELECT * FROM user WHERE username = #{username}")
User findByUsername(String username);
```
2. **输入验证**:对用户输入进行严格验证
3. **数据加密**:敏感数据加密存储
4. **权限控制**:细粒度的权限控制
5. **防止XSS攻击**:对用户输入进行转义
## 第7章 总结与展望
JPA、MyBatis和MyBatis-Plus各有优缺点,适用于不同的项目场景。在选择ORM框架时,应根据项目需求、团队熟悉程度、性能要求等因素综合考虑。
随着技术的发展,ORM框架也在不断演进:
1. **响应式数据访问**:Spring Data R2DBC等响应式数据访问技术的兴起
2. **云原生适配**:更好地支持云原生环境下的数据访问
3. **多模数据库支持**:同时支持关系型和非关系型数据库
4. **AI辅助开发**:利用AI技术自动生成数据访问代码
无论选择哪种ORM框架,理解底层原理、掌握SQL优化技巧、遵循最佳实践都是开发高质量数据访问层的关键。通过合理使用ORM框架,可以显著提高开发效率,降低维护成本,构建高性能、可扩展的企业应用系统。
2782

被折叠的 条评论
为什么被折叠?



