Java 常见对象模型
# Java 常见对象模型
在企业级Java应用开发中,合理使用各种对象模型对于实现代码的高内聚低耦合、提高可维护性和可测试性至关重要。
# 一、DTO(Data Transfer Object):前后端数据传输的桥梁
# 1.1 DTO的定义与作用
DTO是一种纯数据容器,专门用于在不同层之间传输数据,特别是在展示层(前端)与服务层之间。它的主要特点是:
- 仅包含数据字段和对应的getter/setter方法
- 不包含业务逻辑
- 字段设计专注于满足特定接口的数据传输需求
- 可以根据接口需求进行字段过滤、组合或转换
DTO示例代码
/**
* 用户登录数据传输对象
* 用于封装前端提交的登录请求参数
*/
public class UserLoginDTO {
/**
* 用户名/账号
* 前端表单中的username字段值
*/
private String username;
/**
* 用户密码
* 前端表单中的password字段值
*/
private String password;
/**
* 记住登录状态标志
* 前端表单中的rememberMe复选框状态
*/
private Boolean rememberMe;
/**
* 图形验证码
* 安全验证使用,前端表单中的captcha字段值
*/
private String captcha;
/**
* 验证码会话标识
* 用于后端验证图形验证码的有效性
*/
private String captchaId;
// Getter和Setter方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getRememberMe() {
return rememberMe;
}
public void setRememberMe(Boolean rememberMe) {
this.rememberMe = rememberMe;
}
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
public String getCaptchaId() {
return captchaId;
}
public void setCaptchaId(String captchaId) {
this.captchaId = captchaId;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 1.2 DTO在控制器中的使用
/**
* 用户认证控制器
* 处理与用户登录、注册相关的HTTP请求
*/
@RestController
@RequestMapping("/api/auth")
public class AuthController {
/**
* 用户服务,注入用于处理用户认证相关业务逻辑
*/
@Autowired
private UserService userService;
/**
* 用户登录接口
*
* @param loginDTO 登录请求数据传输对象,包含用户提交的登录信息
* @return 登录结果,包含认证令牌和用户基本信息
*/
@PostMapping("/login")
public Result<LoginVO> login(@RequestBody UserLoginDTO loginDTO) {
// 1. 参数校验(实际项目中应该使用@Valid注解结合验证器进行校验)
if (StringUtils.isEmpty(loginDTO.getUsername()) || StringUtils.isEmpty(loginDTO.getPassword())) {
return Result.error("用户名和密码不能为空");
}
// 2. 验证码校验
if (!captchaService.validate(loginDTO.getCaptchaId(), loginDTO.getCaptcha())) {
return Result.error("验证码错误或已过期");
}
// 3. 调用业务逻辑层处理登录
try {
// 注意:这里将DTO传递给服务层进行处理
LoginVO loginResult = userService.login(loginDTO);
return Result.success(loginResult);
} catch (Exception e) {
log.error("用户登录失败: {}", e.getMessage(), e);
return Result.error("登录失败: " + e.getMessage());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
DTO的最佳实践
- 严格遵循单一职责原则,一个DTO只为一个特定接口服务
- 命名应表明其用途,如
UserRegistrationDTO
、OrderCreateDTO
- 对于敏感字段(如密码),不要提供toString()方法,避免日志泄露
- 考虑使用Builder模式创建复杂DTO,提高代码可读性
- 使用Bean Validation注解(如@NotNull、@Size等)进行参数验证
# 二、VO(View Object):后端向前端展示的数据容器
# 2.1 VO的定义与作用
VO是专门用于向前端返回数据的对象,它封装了控制器要返回给用户界面的数据。主要特点:
- 精确控制返回给前端的数据字段
- 可以聚合多个数据源的信息
- 通常比PO(持久化对象)包含更少的字段,避免敏感信息泄露
- 可以包含前端展示所需的格式化数据
VO示例代码
/**
* 用户登录成功后的视图对象
* 封装登录成功后返回给前端的用户信息和认证令牌
*/
public class LoginVO {
/**
* 用户ID
* 系统内部唯一标识
*/
private Long userId;
/**
* 用户显示名称
* 用于前端界面展示
*/
private String displayName;
/**
* 用户头像URL
* 用于前端界面展示用户头像
*/
private String avatarUrl;
/**
* 认证令牌
* 用于后续请求的身份验证
*/
private String token;
/**
* 令牌有效期(秒)
* 前端可据此决定何时刷新令牌
*/
private Long expiresIn;
/**
* 用户权限列表
* 前端可据此控制界面元素的显示/隐藏
*/
private List<String> permissions;
// Getter和Setter方法
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 2.2 VO在服务层中的构建
/**
* 用户服务实现类
* 负责用户相关的业务逻辑处理
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private PermissionService permissionService;
/**
* 处理用户登录业务逻辑
*
* @param loginDTO 登录请求数据
* @return 登录结果视图对象
* @throws AuthenticationException 认证失败时抛出
*/
@Override
public LoginVO login(UserLoginDTO loginDTO) throws AuthenticationException {
// 1. 查询用户信息
User user = userRepository.findByUsername(loginDTO.getUsername())
.orElseThrow(() -> new AuthenticationException("用户不存在"));
// 2. 验证密码
if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
throw new AuthenticationException("密码错误");
}
// 3. 生成认证令牌
String token = tokenProvider.generateToken(user.getId(), user.getUsername());
// 4. 获取用户权限
List<String> permissions = permissionService.getUserPermissions(user.getId());
// 5. 构建并返回VO对象
LoginVO loginVO = new LoginVO();
loginVO.setUserId(user.getId());
loginVO.setDisplayName(user.getNickname() != null ? user.getNickname() : user.getUsername());
loginVO.setAvatarUrl(user.getAvatarUrl());
loginVO.setToken(token);
loginVO.setExpiresIn(tokenProvider.getExpirationInSeconds());
loginVO.setPermissions(permissions);
return loginVO;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
VO的最佳实践
- 避免在VO中包含敏感数据(如密码哈希、内部ID等)
- 可以添加辅助计算方法,但应保持轻量级,不包含复杂业务逻辑
- 使用数据转换工具(如MapStruct、ModelMapper)从其他对象转换为VO
- 为降低网络传输量,可只包含当前视图需要的字段
- 在大型项目中,可根据不同前端视图需求创建多个专用VO
# 三、PO(Persistent Object):数据库实体映射对象
# 3.1 PO的定义与作用
PO是与数据库表结构一一对应的Java对象,代表数据库中的一条记录。主要特点:
- 属性与数据库表的字段严格对应
- 通常包含JPA或MyBatis等ORM框架的注解
- 不包含复杂的业务逻辑
- 是持久层与数据库交互的基本单元
PO示例代码
/**
* 用户实体类
* 映射数据库中的user表
*/
@Entity
@Table(name = "sys_user")
public class User implements Serializable {
/**
* 用户ID,主键
* 数据库自增长
*/
@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 = "nickname", length = 50)
private String nickname;
/**
* 用户头像URL
*/
@Column(name = "avatar_url", length = 255)
private String avatarUrl;
/**
* 用户电子邮箱
*/
@Column(name = "email", length = 100)
private String email;
/**
* 手机号码
*/
@Column(name = "mobile", length = 20)
private String mobile;
/**
* 账号状态:0-禁用,1-启用
*/
@Column(name = "status", nullable = false)
private Integer status;
/**
* 账号创建时间
*/
@Column(name = "create_time", nullable = false)
private LocalDateTime createTime;
/**
* 最后更新时间
*/
@Column(name = "update_time")
private LocalDateTime updateTime;
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// 其他getter和setter方法...
/**
* 判断账号是否启用
*
* @return 账号是否启用
*/
public boolean isEnabled() {
return status != null && status == 1;
}
/**
* 创建/更新前的预处理
*/
@PrePersist
public void prePersist() {
if (createTime == null) {
createTime = LocalDateTime.now();
}
updateTime = LocalDateTime.now();
}
/**
* 更新前的预处理
*/
@PreUpdate
public void preUpdate() {
updateTime = LocalDateTime.now();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 3.2 PO在数据访问层的使用
/**
* 用户数据访问接口
* 定义与用户表相关的数据库操作方法
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 根据用户名查询用户信息
*
* @param username 用户名
* @return 用户对象,使用Optional包装避免空指针异常
*/
Optional<User> findByUsername(String username);
/**
* 判断指定用户名是否已存在
*
* @param username 用户名
* @return 是否存在
*/
boolean existsByUsername(String username);
/**
* 根据邮箱查询用户
*
* @param email 电子邮箱
* @return 用户对象
*/
Optional<User> findByEmail(String email);
/**
* 查询指定状态的用户列表
*
* @param status 账号状态
* @return 用户列表
*/
List<User> findByStatus(Integer status);
/**
* 根据创建时间范围查询用户
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 用户列表
*/
@Query("SELECT u FROM User u WHERE u.createTime BETWEEN :startTime AND :endTime ORDER BY u.createTime DESC")
List<User> findByCreateTimeBetween(@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
PO的最佳实践
- 保持与数据库表的严格对应关系,不添加表中不存在的字段
- 使用JPA等ORM框架的注解标注字段属性,如@Column、@Id等
- 添加适当的验证注解,如@NotNull、@Size等
- 可以添加与实体生命周期相关的钩子方法,如@PrePersist、@PreUpdate
- 尽量不在PO中添加复杂的业务逻辑,保持持久层与业务层的分离
# 四、BO(Business Object):业务对象
# 4.1 BO的定义与作用
BO是封装业务逻辑的对象,通常包含多个领域对象和复杂的业务规则。主要特点:
- 包含业务逻辑处理方法
- 可能聚合多个PO或其他数据
- 不直接与数据库交互,但可以调用DAO进行数据操作
- 是业务层的核心处理单元
BO示例代码
/**
* 用户业务对象
* 封装与用户相关的业务逻辑
*/
public class UserBO {
/**
* 用户基本信息
*/
private User userInfo;
/**
* 用户角色列表
*/
private List<Role> roles;
/**
* 用户权限列表
*/
private Set<String> permissions;
/**
* 用户数据可访问范围
* 比如:部门、区域等
*/
private List<DataScope> dataScopes;
/**
* 最后登录信息
*/
private UserLoginLog lastLoginInfo;
/**
* 构造函数,初始化用户业务对象
*
* @param userInfo 用户基本信息
*/
public UserBO(User userInfo) {
this.userInfo = userInfo;
this.roles = new ArrayList<>();
this.permissions = new HashSet<>();
this.dataScopes = new ArrayList<>();
}
// Getter和Setter方法...
/**
* 判断用户是否拥有指定角色
*
* @param roleCode 角色编码
* @return 是否拥有该角色
*/
public boolean hasRole(String roleCode) {
if (roles == null || roles.isEmpty()) {
return false;
}
return roles.stream().anyMatch(role -> roleCode.equals(role.getCode()));
}
/**
* 判断用户是否拥有指定权限
*
* @param permissionCode 权限编码
* @return 是否拥有该权限
*/
public boolean hasPermission(String permissionCode) {
if (permissions == null || permissions.isEmpty()) {
return false;
}
return permissions.contains(permissionCode);
}
/**
* 计算用户权限等级
* 根据用户角色和权限数量综合评估
*
* @return 权限等级(1-5,5为最高)
*/
public int calculatePermissionLevel() {
// 默认等级为1
int level = 1;
// 根据角色提升等级
if (roles != null) {
for (Role role : roles) {
if ("ADMIN".equals(role.getCode())) {
return 5; // 管理员直接返回最高等级
} else if ("MANAGER".equals(role.getCode())) {
level = Math.max(level, 4);
} else if ("SUPERVISOR".equals(role.getCode())) {
level = Math.max(level, 3);
} else if ("STAFF".equals(role.getCode())) {
level = Math.max(level, 2);
}
}
}
// 根据权限数量微调等级
if (permissions != null && !permissions.isEmpty()) {
if (permissions.size() > 30) {
level = Math.min(5, level + 1);
}
}
return level;
}
/**
* 检查用户是否可以访问指定数据范围
*
* @param scopeType 范围类型(如部门、区域等)
* @param scopeId 范围ID
* @return 是否可访问
*/
public boolean canAccessDataScope(String scopeType, Long scopeId) {
if (dataScopes == null || dataScopes.isEmpty()) {
return false;
}
return dataScopes.stream()
.anyMatch(scope -> scopeType.equals(scope.getType()) &&
(scope.isAllData() || scope.getScopeIds().contains(scopeId)));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 4.2 BO在服务层中的使用
/**
* 用户授权服务实现类
* 处理与用户授权相关的业务逻辑
*/
@Service
public class AuthorizationServiceImpl implements AuthorizationService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PermissionRepository permissionRepository;
@Autowired
private DataScopeRepository dataScopeRepository;
/**
* 获取用户授权信息
*
* @param userId 用户ID
* @return 用户业务对象,包含完整的授权信息
*/
@Override
public UserBO getUserAuthorization(Long userId) {
// 1. 获取用户基本信息
User user = userRepository.findById(userId)
.orElseThrow(() -> new ServiceException("用户不存在"));
// 2. 创建用户业务对象
UserBO userBO = new UserBO(user);
// 3. 获取用户角色信息
List<Role> roles = roleRepository.findRolesByUserId(userId);
userBO.setRoles(roles);
// 4. 获取用户权限信息
Set<String> permissions = permissionRepository.findPermissionCodesByUserId(userId);
userBO.setPermissions(permissions);
// 5. 获取用户数据访问范围
List<DataScope> dataScopes = dataScopeRepository.findByUserId(userId);
userBO.setDataScopes(dataScopes);
return userBO;
}
/**
* 检查用户是否有权限访问特定资源
*
* @param userId 用户ID
* @param resourceType 资源类型
* @param resourceId 资源ID
* @param requiredPermission 所需权限
* @return 是否有权限访问
*/
@Override
public boolean checkUserAccess(Long userId, String resourceType,
Long resourceId, String requiredPermission) {
// 1. 获取用户授权信息
UserBO userBO = getUserAuthorization(userId);
// 2. 检查用户状态
if (!userBO.getUserInfo().isEnabled()) {
return false; // 用户被禁用,无权访问
}
// 3. 检查是否为管理员(拥有所有权限)
if (userBO.hasRole("ADMIN")) {
return true;
}
// 4. 检查是否有所需的具体权限
if (!userBO.hasPermission(requiredPermission)) {
return false;
}
// 5. 检查是否有权访问特定资源(数据权限)
return userBO.canAccessDataScope(resourceType, resourceId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
BO的最佳实践
- 聚焦于业务逻辑的封装,避免与持久层直接交互
- 可以聚合多个领域对象,但要保持业务内聚性
- 实现复杂的业务规则和计算逻辑
- 在业务对象之间可以相互协作,但要避免循环依赖
- 可以考虑使用工厂模式或建造者模式创建复杂的BO
# 五、DO(Domain Object):领域对象
# 5.1 DO的定义与作用
DO是领域驱动设计(DDD)中的概念,代表业务领域中的实体或值对象。主要特点:
- 具有唯一标识(实体)或不可变性(值对象)
- 包含领域规则和行为
- 是领域模型的核心组成部分
- 与具体技术实现无关,专注于业务概念
DO示例代码
/**
* 订单领域对象
* 代表业务领域中的订单概念
*/
public class OrderDO {
/**
* 订单ID(领域标识符)
*/
private final OrderId id;
/**
* 订单所属客户ID
*/
private final CustomerId customerId;
/**
* 订单项列表
*/
private final List<OrderItemDO> items;
/**
* 订单状态
*/
private OrderStatus status;
/**
* 订单创建时间
*/
private final LocalDateTime createdAt;
/**
* 订单总金额
*/
private Money totalAmount;
/**
* 订单折扣金额
*/
private Money discountAmount;
/**
* 最后修改时间
*/
private LocalDateTime updatedAt;
/**
* 创建新订单
*
* @param id 订单ID
* @param customerId 客户ID
* @param items 订单项列表
*/
public OrderDO(OrderId id, CustomerId customerId, List<OrderItemDO> items) {
// 参数验证
if (id == null) {
throw new IllegalArgumentException("订单ID不能为空");
}
if (customerId == null) {
throw new IllegalArgumentException("客户ID不能为空");
}
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("订单项不能为空");
}
this.id = id;
this.customerId = customerId;
this.items = new ArrayList<>(items);
this.status = OrderStatus.CREATED;
this.createdAt = LocalDateTime.now();
this.updatedAt = this.createdAt;
// 计算订单金额
calculateAmount();
}
/**
* 计算订单金额
*/
private void calculateAmount() {
// 计算订单总金额(所有订单项金额总和)
this.totalAmount = items.stream()
.map(OrderItemDO::getSubtotal)
.reduce(Money.ZERO, Money::add);
// 折扣金额默认为零
this.discountAmount = Money.ZERO;
}
/**
* 添加订单项
*
* @param item 订单项
* @return 添加成功返回true,否则返回false
*/
public boolean addItem(OrderItemDO item) {
// 检查订单状态,只有创建状态的订单才能添加商品
if (status != OrderStatus.CREATED) {
return false;
}
// 检查订单项是否有效
if (item == null) {
return false;
}
// 添加订单项并重新计算金额
items.add(item);
calculateAmount();
updatedAt = LocalDateTime.now();
return true;
}
/**
* 应用折扣
*
* @param discount 折扣金额
* @return 应用成功返回true,否则返回false
*/
public boolean applyDiscount(Money discount) {
// 检查订单状态
if (status != OrderStatus.CREATED) {
return false;
}
// 折扣金额不能为负
if (discount.isNegative()) {
return false;
}
// 折扣金额不能大于订单总金额
if (discount.isGreaterThan(totalAmount)) {
return false;
}
// 应用折扣
this.discountAmount = discount;
updatedAt = LocalDateTime.now();
return true;
}
/**
* 提交订单
*
* @return 提交成功返回true,否则返回false
*/
public boolean submit() {
// 只有创建状态的订单才能提交
if (status != OrderStatus.CREATED) {
return false;
}
// 修改订单状态
status = OrderStatus.SUBMITTED;
updatedAt = LocalDateTime.now();
return true;
}
/**
* 支付订单
*
* @return 支付成功返回true,否则返回false
*/
public boolean pay() {
// 只有已提交的订单才能支付
if (status != OrderStatus.SUBMITTED) {
return false;
}
// 修改订单状态
status = OrderStatus.PAID;
updatedAt = LocalDateTime.now();
return true;
}
/**
* 取消订单
*
* @return 取消成功返回true,否则返回false
*/
public boolean cancel() {
// 已支付或已完成的订单不能取消
if (status == OrderStatus.PAID || status == OrderStatus.COMPLETED) {
return false;
}
// 修改订单状态
status = OrderStatus.CANCELLED;
updatedAt = LocalDateTime.now();
return true;
}
/**
* 获取应付金额(总金额减去折扣)
*
* @return 应付金额
*/
public Money getPayableAmount() {
return totalAmount.subtract(discountAmount);
}
// Getter方法
// 注意:领域对象通常不提供setter方法,而是通过行为方法修改状态
public OrderId getId() {
return id;
}
public CustomerId getCustomerId() {
return customerId;
}
public List<OrderItemDO> getItems() {
// 返回不可修改的列表,防止外部直接修改
return Collections.unmodifiableList(items);
}
public OrderStatus getStatus() {
return status;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public Money getTotalAmount() {
return totalAmount;
}
public Money getDiscountAmount() {
return discountAmount;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
/**
* 订单状态枚举
*/
public enum OrderStatus {
CREATED, // 已创建
SUBMITTED, // 已提交
PAID, // 已支付
SHIPPED, // 已发货
COMPLETED, // 已完成
CANCELLED // 已取消
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# 5.2 DO在领域服务中的使用
/**
* 订单领域服务
* 处理与订单相关的核心业务逻辑
*/
@Service
public class OrderDomainService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private CustomerRepository customerRepository;
@Autowired
private ProductRepository productRepository;
/**
* 创建新订单
*
* @param customerId 客户ID
* @param items 订单项列表(产品ID和数量)
* @return 创建的订单
* @throws DomainException 如果创建失败
*/
public OrderDO createOrder(Long customerId, List<OrderItemRequest> items) {
// 1. 验证客户存在
CustomerId customerIdObj = new CustomerId(customerId);
if (!customerRepository.exists(customerIdObj)) {
throw new DomainException("客户不存在");
}
// 2. 验证并转换订单项
List<OrderItemDO> orderItems = new ArrayList<>();
for (OrderItemRequest itemRequest : items) {
// 验证产品存在
ProductId productId = new ProductId(itemRequest.getProductId());
ProductDO product = productRepository.findById(productId)
.orElseThrow(() -> new DomainException("产品不存在: " + productId));
// 验证库存充足
if (product.getStock() < itemRequest.getQuantity()) {
throw new DomainException("产品库存不足: " + product.getName());
}
// 创建订单项
OrderItemDO orderItem = new OrderItemDO(
product.getId(),
product.getName(),
product.getPrice(),
itemRequest.getQuantity()
);
orderItems.add(orderItem);
}
// 3. 创建订单领域对象
OrderId orderId = orderRepository.nextId();
OrderDO order = new OrderDO(orderId, customerIdObj, orderItems);
// 4. 保存订单
orderRepository.save(order);
return order;
}
/**
* 应用订单折扣
*
* @param orderId 订单ID
* @param discountAmount 折扣金额
* @return 更新后的订单
* @throws DomainException 如果操作失败
*/
public OrderDO applyDiscount(Long orderId, BigDecimal discountAmount) {
// 1. 获取订单
OrderId orderIdObj = new OrderId(orderId);
OrderDO order = orderRepository.findById(orderIdObj)
.orElseThrow(() -> new DomainException("订单不存在"));
// 2. 应用折扣
Money discount = new Money(discountAmount);
boolean applied = order.applyDiscount(discount);
if (!applied) {
throw new DomainException("无法应用折扣,订单状态可能不允许或折扣金额无效");
}
// 3. 保存更新后的订单
orderRepository.save(order);
return order;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
DO的最佳实践
- 专注于领域规则和业务行为,不关注持久化细节
- 保持状态一致性,通过行为方法而非setter修改状态
- 实现充血模型,将业务逻辑封装在领域对象中
- 使用值对象表示没有唯一标识的概念(如金额、日期等)
- 领域对象之间的关系应反映业务概念之间的关系
- 使用工厂方法创建复杂的领域对象
- 遵循"不变性优先"原则,尽可能使用不可变的值对象
# 六、TO(Transfer Object):系统间数据传输对象
# 6.1 TO的定义与作用
TO是在不同系统或服务之间传输数据的对象,特别适用于微服务架构。主要特点:
- 专为系统间通信设计,关注序列化/反序列化效率
- 通常包含版本信息,支持兼容性处理
- 可能包含与传输相关的元数据
- 相比DTO更注重系统间而非层间的数据传输
TO示例代码
/**
* 订单传输对象
* 用于微服务之间传输订单数据
*/
public class OrderTO implements Serializable {
/**
* 序列化版本号
* 用于在类结构变更时保持兼容性
*/
private static final long serialVersionUID = 1L;
/**
* 数据版本
* 用于不同版本服务之间的兼容性处理
*/
private Integer version = 1;
/**
* 消息ID
* 用于跟踪消息处理和避免重复处理
*/
private String messageId;
/**
* 发送时间戳
* 用于超时处理和排序
*/
private Long timestamp;
/**
* 订单ID
*/
private String orderId;
/**
* 客户ID
*/
private String customerId;
/**
* 订单状态
*/
private String status;
/**
* 订单创建时间
*/
private Long createTime;
/**
* 订单总金额(分)
*/
private Long totalAmount;
/**
* 折扣金额(分)
*/
private Long discountAmount;
/**
* 应付金额(分)
*/
private Long payableAmount;
/**
* 订单项列表
*/
private List<OrderItemTO> items;
/**
* 附加属性
* 用于传递无法预知的扩展字段
*/
private Map<String, Object> attributes;
/**
* 默认构造函数
* 序列化/反序列化需要
*/
public OrderTO() {
this.timestamp = System.currentTimeMillis();
this.messageId = UUID.randomUUID().toString();
this.attributes = new HashMap<>();
}
// Getter和Setter方法
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
// 其他getter和setter方法...
/**
* 添加属性
*
* @param key 属性键
* @param value 属性值
*/
public void addAttribute(String key, Object value) {
if (attributes == null) {
attributes = new HashMap<>();
}
attributes.put(key, value);
}
/**
* 获取属性
*
* @param key 属性键
* @return 属性值
*/
public Object getAttribute(String key) {
return attributes != null ? attributes.get(key) : null;
}
/**
* 订单项传输对象
* 内部类,表示订单中的商品项
*/
public static class OrderItemTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 商品ID
*/
private String productId;
/**
* 商品名称
*/
private String productName;
/**
* 商品单价(分)
*/
private Long unitPrice;
/**
* 商品数量
*/
private Integer quantity;
/**
* 小计金额(分)
*/
private Long subtotal;
// Getter和Setter方法...
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# 6.2 TO在微服务通信中的使用
/**
* 订单消息发送服务
* 负责将订单数据发送到消息队列
*/
@Service
public class OrderMessageService {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Autowired
private ObjectMapper objectMapper;
/**
* 发送订单创建消息到消息队列
*
* @param order 订单领域对象
*/
public void sendOrderCreatedMessage(OrderDO order) {
try {
// 1. 将领域对象转换为传输对象
OrderTO orderTO = convertToTransferObject(order);
// 2. 设置消息元数据
orderTO.setMessageId(UUID.randomUUID().toString());
orderTO.setTimestamp(System.currentTimeMillis());
orderTO.addAttribute("eventType", "ORDER_CREATED");
// 3. 将传输对象序列化为JSON
String payload = objectMapper.writeValueAsString(orderTO);
// 4. 发送消息到Kafka
kafkaTemplate.send("order-events", order.getId().toString(), payload);
log.info("订单创建消息已发送, orderId: {}, messageId: {}",
order.getId(), orderTO.getMessageId());
} catch (Exception e) {
log.error("发送订单创建消息失败", e);
throw new MessageSendException("发送订单消息失败", e);
}
}
/**
* 将订单领域对象转换为传输对象
*
* @param order 订单领域对象
* @return 订单传输对象
*/
private OrderTO convertToTransferObject(OrderDO order) {
OrderTO to = new OrderTO();
// 设置基本信息
to.setOrderId(order.getId().toString());
to.setCustomerId(order.getCustomerId().toString());
to.setStatus(order.getStatus().name());
to.setCreateTime(order.getCreatedAt().toInstant(ZoneOffset.UTC).toEpochMilli());
// 设置金额信息(领域对象中的Money转换为Long)
to.setTotalAmount(order.getTotalAmount().getAmountInCents());
to.setDiscountAmount(order.getDiscountAmount().getAmountInCents());
to.setPayableAmount(order.getPayableAmount().getAmountInCents());
// 转换订单项
List<OrderItemTO> itemTOs = order.getItems().stream()
.map(item -> {
OrderItemTO itemTO = new OrderItemTO();
itemTO.setProductId(item.getProductId().toString());
itemTO.setProductName(item.getProductName());
itemTO.setUnitPrice(item.getUnitPrice().getAmountInCents());
itemTO.setQuantity(item.getQuantity());
itemTO.setSubtotal(item.getSubtotal().getAmountInCents());
return itemTO;
})
.collect(Collectors.toList());
to.setItems(itemTOs);
return to;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
TO的最佳实践
- 设计时考虑版本兼容性,添加版本号字段
- 使用序列化友好的数据类型,如基本类型或String
- 保持轻量级,只传输必要的数据
- 添加消息ID或时间戳等元数据,便于跟踪和处理
- 考虑使用模式(Schema)定义,如Protocol Buffers或Avro
- 提供扩展机制(如属性映射),以适应未来需求变化
- 在微服务架构中,可为不同服务定义专用的TO
# 七、POJO(Plain Old Java Object):简单Java对象
# 7.1 POJO的定义与作用
POJO是不依赖于特定框架或接口的简单Java对象。主要特点:
- 没有特殊限制,不需要实现特定接口或继承特定类
- 通常包含属性和对应的getter/setter方法
- 可以有简单的业务方法,但不应依赖于框架API
- 是其他对象模型(DTO、VO、PO等)的基础
POJO示例代码
/**
* 用户简单Java对象
* 不依赖于任何框架或特定接口
*/
public class UserPOJO {
/**
* 用户ID
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 电子邮箱
*/
private String email;
/**
* 年龄
*/
private Integer age;
/**
* 创建时间
*/
private Date createTime;
/**
* 默认构造函数
*/
public UserPOJO() {
}
/**
* 带参数的构造函数
*
* @param id 用户ID
* @param username 用户名
* @param email 电子邮箱
* @param age 年龄
*/
public UserPOJO(Long id, String username, String email, Integer age) {
this.id = id;
this.username = username;
this.email = email;
this.age = age;
this.createTime = new Date();
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
/**
* 判断用户是否成年
*
* @return 是否成年
*/
public boolean isAdult() {
return age != null && age >= 18;
}
/**
* 获取用户的邮箱服务提供商
*
* @return 邮箱服务提供商,如果邮箱格式无效则返回null
*/
public String getEmailProvider() {
if (email == null || !email.contains("@")) {
return null;
}
return email.substring(email.indexOf('@') + 1);
}
/**
* 计算用户注册天数
*
* @return 注册天数
*/
public long getDaysRegistered() {
if (createTime == null) {
return 0;
}
long diffInMillies = new Date().getTime() - createTime.getTime();
return TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
}
@Override
public String toString() {
return "UserPOJO{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", age=" + age +
", createTime=" + createTime +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserPOJO userPOJO = (UserPOJO) o;
return Objects.equals(id, userPOJO.id) &&
Objects.equals(username, userPOJO.username) &&
Objects.equals(email, userPOJO.email) &&
Objects.equals(age, userPOJO.age) &&
Objects.equals(createTime, userPOJO.createTime);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email, age, createTime);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 7.2 POJO的使用场景
/**
* POJO使用示例
* 展示POJO作为数据容器的灵活性
*/
public class POJOUsageExample {
/**
* 使用POJO作为数据容器
*/
public void demonstratePOJOUsage() {
// 创建POJO实例
UserPOJO user = new UserPOJO();
user.setId(1L);
user.setUsername("johndoe");
user.setEmail("john.doe@example.com");
user.setAge(25);
user.setCreateTime(new Date());
// 使用POJO的业务方法
boolean isAdult = user.isAdult();
String emailProvider = user.getEmailProvider();
long daysRegistered = user.getDaysRegistered();
System.out.println("用户: " + user.getUsername());
System.out.println("是否成年: " + isAdult);
System.out.println("邮箱提供商: " + emailProvider);
System.out.println("注册天数: " + daysRegistered);
// 可以轻松转换为其他对象类型
UserDTO userDTO = convertToDTO(user);
UserVO userVO = convertToVO(user);
// 可以轻松序列化为JSON或其他格式
String json = convertToJson(user);
System.out.println("JSON表示: " + json);
}
/**
* 将POJO转换为DTO
*
* @param pojo 用户POJO
* @return 用户DTO
*/
private UserDTO convertToDTO(UserPOJO pojo) {
UserDTO dto = new UserDTO();
dto.setUsername(pojo.getUsername());
dto.setEmail(pojo.getEmail());
return dto;
}
/**
* 将POJO转换为VO
*
* @param pojo 用户POJO
* @return 用户VO
*/
private UserVO convertToVO(UserPOJO pojo) {
UserVO vo = new UserVO();
vo.setId(pojo.getId());
vo.setUsername(pojo.getUsername());
vo.setDaysRegistered(pojo.getDaysRegistered());
return vo;
}
/**
* 将POJO转换为JSON字符串
*
* @param pojo 用户POJO
* @return JSON字符串
*/
private String convertToJson(UserPOJO pojo) {
// 在实际应用中,可以使用Jackson或Gson等库进行转换
// 这里简化为手动构建JSON字符串
return "{" +
"\"id\":" + pojo.getId() + "," +
"\"username\":\"" + pojo.getUsername() + "\"," +
"\"email\":\"" + pojo.getEmail() + "\"," +
"\"age\":" + pojo.getAge() + "," +
"\"createTime\":\"" + pojo.getCreateTime() + "\"" +
"}";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
POJO的最佳实践
- 保持简单,不依赖于特定框架
- 实现基本的Object方法(equals、hashCode、toString)
- 提供默认构造函数和必要的getter/setter方法
- 可以包含简单的业务逻辑方法,但应保持自包含
- 作为其他对象模型的基础,便于转换和扩展
- 考虑实现Serializable接口,增强序列化能力
- 避免在POJO中引入过多依赖
# 八、DAO(Data Access Object):数据访问对象
# 8.1 DAO的定义与作用
DAO是负责与数据库交互的对象,封装了数据访问逻辑。主要特点:
- 提供数据访问接口,隐藏数据库操作细节
- 通常包含CRUD(增删改查)操作方法
- 与PO(持久化对象)密切配合
- 是业务逻辑层与数据库之间的桥梁
# 8.2 DAO接口代码
/**
* 用户数据访问对象接口
* 定义与用户相关的数据库操作方法
*/
public interface UserDAO {
/**
* 根据ID查询用户
*
* @param id 用户ID
* @return 用户对象,如果不存在则返回null
*/
User findById(Long id);
/**
* 根据用户名查询用户
*
* @param username 用户名
* @return 用户对象,如果不存在则返回null
*/
User findByUsername(String username);
/**
* 保存用户信息
* 如果用户ID为null,则插入新记录;否则更新已有记录
*
* @param user 用户对象
* @return 保存后的用户对象(包含数据库生成的ID)
*/
User save(User user);
/**
* 根据ID删除用户
*
* @param id 用户ID
* @return 是否删除成功
*/
boolean deleteById(Long id);
/**
* 查询符合条件的用户列表
*
* @param criteria 查询条件
* @param pageable 分页参数
* @return 用户列表和分页信息
*/
Page<User> findByCriteria(UserSearchCriteria criteria, Pageable pageable);
/**
* 根据状态查询用户数量
*
* @param status 用户状态
* @return 符合状态的用户数量
*/
long countByStatus(Integer status);
/**
* 批量保存用户
*
* @param users 用户列表
* @return 保存后的用户列表
*/
List<User> saveAll(List<User> users);
/**
* 更新用户状态
*
* @param id 用户ID
* @param status 新状态
* @return 是否更新成功
*/
boolean updateStatus(Long id, Integer status);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 8.3 DAO实现类代码
/**
* 用户数据访问对象JDBC实现
* 基于JDBC实现与数据库的交互
*/
@Repository
public class UserDAOJdbcImpl implements UserDAO {
/**
* JDBC模板,用于执行SQL语句
*/
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 用户行映射器,将结果集行映射为User对象
*/
private final RowMapper<User> userRowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setNickname(rs.getString("nickname"));
user.setEmail(rs.getString("email"));
user.setMobile(rs.getString("mobile"));
user.setStatus(rs.getInt("status"));
user.setAvatarUrl(rs.getString("avatar_url"));
// 处理日期类型
Timestamp createTime = rs.getTimestamp("create_time");
if (createTime != null) {
user.setCreateTime(createTime.toLocalDateTime());
}
Timestamp updateTime = rs.getTimestamp("update_time");
if (updateTime != null) {
user.setUpdateTime(updateTime.toLocalDateTime());
}
return user;
};
/**
* 根据ID查询用户
*/
@Override
public User findById(Long id) {
try {
return jdbcTemplate.queryForObject(
"SELECT * FROM sys_user WHERE id = ?",
userRowMapper,
id
);
} catch (EmptyResultDataAccessException e) {
// 查询结果为空时返回null
return null;
}
}
/**
* 根据用户名查询用户
*/
@Override
public User findByUsername(String username) {
try {
return jdbcTemplate.queryForObject(
"SELECT * FROM sys_user WHERE username = ?",
userRowMapper,
username
);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
/**
* 保存用户信息
*/
@Override
public User save(User user) {
if (user.getId() == null) {
// 插入新用户
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO sys_user (username, password, nickname, email, mobile, status, avatar_url, create_time, update_time) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
Statement.RETURN_GENERATED_KEYS
);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getNickname());
ps.setString(4, user.getEmail());
ps.setString(5, user.getMobile());
ps.setInt(6, user.getStatus());
ps.setString(7, user.getAvatarUrl());
LocalDateTime now = LocalDateTime.now();
ps.setTimestamp(8, Timestamp.valueOf(now));
ps.setTimestamp(9, Timestamp.valueOf(now));
return ps;
}, keyHolder);
// 设置自动生成的主键
user.setId(keyHolder.getKey().longValue());
} else {
// 更新已有用户
jdbcTemplate.update(
"UPDATE sys_user SET username = ?, password = ?, nickname = ?, " +
"email = ?, mobile = ?, status = ?, avatar_url = ?, update_time = ? " +
"WHERE id = ?",
user.getUsername(),
user.getPassword(),
user.getNickname(),
user.getEmail(),
user.getMobile(),
user.getStatus(),
user.getAvatarUrl(),
Timestamp.valueOf(LocalDateTime.now()),
user.getId()
);
}
return user;
}
/**
* 根据ID删除用户
*/
@Override
public boolean deleteById(Long id) {
int rowsAffected = jdbcTemplate.update(
"DELETE FROM sys_user WHERE id = ?",
id
);
return rowsAffected > 0;
}
/**
* 查询符合条件的用户列表
*/
@Override
public Page<User> findByCriteria(UserSearchCriteria criteria, Pageable pageable) {
// 构建动态查询SQL
StringBuilder sql = new StringBuilder("SELECT * FROM sys_user WHERE 1=1");
List<Object> params = new ArrayList<>();
// 添加条件
if (criteria.getUsername() != null && !criteria.getUsername().isEmpty()) {
sql.append(" AND username LIKE ?");
params.add("%" + criteria.getUsername() + "%");
}
if (criteria.getEmail() != null && !criteria.getEmail().isEmpty()) {
sql.append(" AND email LIKE ?");
params.add("%" + criteria.getEmail() + "%");
}
if (criteria.getStatus() != null) {
sql.append(" AND status = ?");
params.add(criteria.getStatus());
}
// 添加排序
if (pageable.getSort() != null && !pageable.getSort().isEmpty()) {
sql.append(" ORDER BY");
boolean first = true;
for (Sort.Order order : pageable.getSort()) {
if (!first) {
sql.append(",");
}
sql.append(" ").append(order.getProperty())
.append(" ").append(order.getDirection().name());
first = false;
}
}
// 添加分页
sql.append(" LIMIT ?, ?");
params.add(pageable.getOffset());
params.add(pageable.getPageSize());
// 执行查询
List<User> users = jdbcTemplate.query(
sql.toString(),
userRowMapper,
params.toArray()
);
// 查询总记录数
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM sys_user WHERE 1=1");
List<Object> countParams = new ArrayList<>();
if (criteria.getUsername() != null && !criteria.getUsername().isEmpty()) {
countSql.append(" AND username LIKE ?");
countParams.add("%" + criteria.getUsername() + "%");
}
if (criteria.getEmail() != null && !criteria.getEmail().isEmpty()) {
countSql.append(" AND email LIKE ?");
countParams.add("%" + criteria.getEmail() + "%");
}
if (criteria.getStatus() != null) {
countSql.append(" AND status = ?");
countParams.add(criteria.getStatus());
}
long total = jdbcTemplate.queryForObject(
countSql.toString(),
Long.class,
countParams.toArray()
);
// 构建并返回分页结果
return new PageImpl<>(users, pageable, total);
}
/**
* 根据状态查询用户数量
*/
@Override
public long countByStatus(Integer status) {
return jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM sys_user WHERE status = ?",
Long.class,
status
);
}
/**
* 批量保存用户
*/
@Override
public List<User> saveAll(List<User> users) {
if (users == null || users.isEmpty()) {
return Collections.emptyList();
}
// 使用批处理提高性能
jdbcTemplate.batchUpdate(
"INSERT INTO sys_user (username, password, nickname, email, mobile, status, avatar_url, create_time, update_time) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE " +
"username = VALUES(username), password = VALUES(password), nickname = VALUES(nickname), " +
"email = VALUES(email), mobile = VALUES(mobile), status = VALUES(status), " +
"avatar_url = VALUES(avatar_url), update_time = VALUES(update_time)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getNickname());
ps.setString(4, user.getEmail());
ps.setString(5, user.getMobile());
ps.setInt(6, user.getStatus());
ps.setString(7, user.getAvatarUrl());
LocalDateTime now = LocalDateTime.now();
ps.setTimestamp(8, Timestamp.valueOf(now));
ps.setTimestamp(9, Timestamp.valueOf(now));
}
@Override
public int getBatchSize() {
return users.size();
}
}
);
return users;
}
/**
* 更新用户状态
*/
@Override
public boolean updateStatus(Long id, Integer status) {
int rowsAffected = jdbcTemplate.update(
"UPDATE sys_user SET status = ?, update_time = ? WHERE id = ?",
status,
Timestamp.valueOf(LocalDateTime.now()),
id
);
return rowsAffected > 0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# 8.4 DAO在服务层中的使用
/**
* 用户服务实现类
* 使用DAO访问数据库,实现业务逻辑
*/
@Service
public class UserServiceImpl implements UserService {
/**
* 用户数据访问对象
* 注入依赖用于访问用户数据
*/
@Autowired
private UserDAO userDAO;
/**
* 密码编码器
* 用于加密用户密码
*/
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 用户注册
*
* @param registerDTO 注册信息
* @return 注册结果
*/
@Override
@Transactional
public UserVO register(UserRegisterDTO registerDTO) {
// 1. 检查用户名是否已存在
User existingUser = userDAO.findByUsername(registerDTO.getUsername());
if (existingUser != null) {
throw new BusinessException("用户名已存在");
}
// 2. 创建新用户
User user = new User();
user.setUsername(registerDTO.getUsername());
user.setPassword(passwordEncoder.encode(registerDTO.getPassword())); // 密码加密
user.setNickname(registerDTO.getNickname());
user.setEmail(registerDTO.getEmail());
user.setMobile(registerDTO.getMobile());
user.setStatus(1); // 默认状态:启用
// 3. 保存用户
User savedUser = userDAO.save(user);
// 4. 转换为视图对象返回
UserVO userVO = new UserVO();
userVO.setId(savedUser.getId());
userVO.setUsername(savedUser.getUsername());
userVO.setNickname(savedUser.getNickname());
return userVO;
}
/**
* 根据用户名查询用户
*
* @param username 用户名
* @return 用户信息
*/
@Override
public UserVO findByUsername(String username) {
User user = userDAO.findByUsername(username);
if (user == null) {
return null;
}
// 转换为视图对象返回
UserVO userVO = new UserVO();
userVO.setId(user.getId());
userVO.setUsername(user.getUsername());
userVO.setNickname(user.getNickname());
userVO.setEmail(user.getEmail());
userVO.setMobile(user.getMobile());
userVO.setStatus(user.getStatus());
userVO.setAvatarUrl(user.getAvatarUrl());
return userVO;
}
/**
* 更新用户信息
*
* @param id 用户ID
* @param updateDTO 更新信息
* @return 更新结果
*/
@Override
@Transactional
public UserVO updateUser(Long id, UserUpdateDTO updateDTO) {
// 1. 查询用户是否存在
User user = userDAO.findById(id);
if (user == null) {
throw new BusinessException("用户不存在");
}
// 2. 更新用户信息
if (updateDTO.getNickname() != null) {
user.setNickname(updateDTO.getNickname());
}
if (updateDTO.getEmail() != null) {
user.setEmail(updateDTO.getEmail());
}
if (updateDTO.getMobile() != null) {
user.setMobile(updateDTO.getMobile());
}
if (updateDTO.getAvatarUrl() != null) {
user.setAvatarUrl(updateDTO.getAvatarUrl());
}
// 3. 保存更新后的用户信息
User updatedUser = userDAO.save(user);
// 4. 转换为视图对象返回
UserVO userVO = new UserVO();
userVO.setId(updatedUser.getId());
userVO.setUsername(updatedUser.getUsername());
userVO.setNickname(updatedUser.getNickname());
userVO.setEmail(updatedUser.getEmail());
userVO.setMobile(updatedUser.getMobile());
userVO.setStatus(updatedUser.getStatus());
userVO.setAvatarUrl(updatedUser.getAvatarUrl());
return userVO;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
DAO的最佳实践
- 保持接口和实现的分离,便于切换数据源
- 使用统一的异常处理机制,避免泄露数据库异常
- 添加适当的日志记录,便于问题排查
- 使用声明式事务管理,确保数据一致性
- 考虑使用ORM框架,减少重复的CRUD代码
- 为复杂查询提供专用方法,避免在服务层拼接SQL
- 实现分页和排序功能,提高查询性能
# 九、对象模型的选择与转换
# 9.1 各种对象模型的比较
对象类型 | 主要用途 | 生命周期 | 关注点 | 相关技术/框架 |
---|---|---|---|---|
DTO | 层间数据传输,尤其是前后端交互 | 请求-响应周期 | 数据传输效率、序列化 | Jackson, JSON-B |
VO | 视图数据展示 | 响应周期 | 前端展示需求、数据过滤 | Jackson, 模板引擎 |
PO | 数据库记录映射 | 持久化生命周期 | 数据库结构、字段映射 | JPA, MyBatis, JDBC |
BO | 业务逻辑封装 | 业务处理周期 | 业务规则、聚合业务数据 | Spring服务 |
DO | 领域概念表达 | 领域模型周期 | 业务规则、实体完整性 | DDD, Spring |
TO | 系统间通信 | 跨系统传输周期 | 序列化效率、版本兼容 | REST, gRPC, Kafka |
POJO | 基础数据结构 | 不限 | 简单性、可重用性 | 标准Java |
DAO | 数据访问操作 | 应用程序生命周期 | 数据库操作封装、查询效率 | JDBC, JPA, MyBatis |
# 9.2 对象转换最佳实践
对象之间的转换是企业应用中常见的操作,下面是一些最佳实践:
/**
* 用户对象转换工具类
* 负责各种用户相关对象模型之间的转换
*/
@Component
public class UserConverter {
/**
* 将PO转换为VO
*
* @param user 用户持久化对象
* @return 用户视图对象
*/
public UserVO convertToVO(User user) {
if (user == null) {
return null;
}
UserVO vo = new UserVO();
vo.setId(user.getId());
vo.setUsername(user.getUsername());
vo.setNickname(user.getNickname());
vo.setEmail(user.getEmail());
vo.setMobile(user.getMobile());
vo.setStatus(user.getStatus());
vo.setAvatarUrl(user.getAvatarUrl());
// 添加视图层特定的计算字段
if (user.getCreateTime() != null) {
vo.setRegisterDays(ChronoUnit.DAYS.between(
user.getCreateTime(), LocalDateTime.now()));
}
return vo;
}
/**
* 将注册DTO转换为PO
*
* @param dto 用户注册数据传输对象
* @param passwordEncoder 密码编码器
* @return 用户持久化对象
*/
public User convertToPO(UserRegisterDTO dto, PasswordEncoder passwordEncoder) {
if (dto == null) {
return null;
}
User user = new User();
user.setUsername(dto.getUsername());
// 密码加密
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setNickname(dto.getNickname());
user.setEmail(dto.getEmail());
user.setMobile(dto.getMobile());
user.setStatus(1); // 默认状态:启用
return user;
}
/**
* 将PO转换为BO
*
* @param user 用户持久化对象
* @param roles 用户角色列表
* @param permissions 用户权限集合
* @return 用户业务对象
*/
public UserBO convertToBO(User user, List<Role> roles, Set<String> permissions) {
if (user == null) {
return null;
}
UserBO bo = new UserBO(user);
bo.setRoles(roles);
bo.setPermissions(permissions);
return bo;
}
/**
* 将PO转换为TO
* 用于系统间数据传输
*
* @param user 用户持久化对象
* @return 用户传输对象
*/
public UserTO convertToTO(User user) {
if (user == null) {
return null;
}
UserTO to = new UserTO();
to.setUserId(user.getId().toString());
to.setUsername(user.getUsername());
to.setNickname(user.getNickname());
to.setEmail(user.getEmail());
to.setStatus(user.getStatus().toString());
// 添加元数据
to.setVersion(1);
to.setTimestamp(System.currentTimeMillis());
to.setMessageId(UUID.randomUUID().toString());
return to;
}
/**
* 批量转换PO到VO
*
* @param users 用户持久化对象列表
* @return 用户视图对象列表
*/
public List<UserVO> convertToVOList(List<User> users) {
if (users == null) {
return Collections.emptyList();
}
return users.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
}
/**
* 将更新DTO应用到PO
*
* @param user 待更新的用户持久化对象
* @param dto 用户更新数据传输对象
*/
public void updatePOFromDTO(User user, UserUpdateDTO dto) {
if (user == null || dto == null) {
return;
}
// 只更新非空字段
if (dto.getNickname() != null) {
user.setNickname(dto.getNickname());
}
if (dto.getEmail() != null) {
user.setEmail(dto.getEmail());
}
if (dto.getMobile() != null) {
user.setMobile(dto.getMobile());
}
if (dto.getAvatarUrl() != null) {
user.setAvatarUrl(dto.getAvatarUrl());
}
// 更新修改时间
user.setUpdateTime(LocalDateTime.now());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# 9.3 使用MapStruct简化对象转换
MapStruct是一个代码生成工具,可以大幅简化对象转换代码:
/**
* 用户对象映射器
* 使用MapStruct自动生成对象转换代码
*/
@Mapper(componentModel = "spring")
public interface UserMapper {
/**
* 将用户PO转换为VO
*
* @param user 用户持久化对象
* @return 用户视图对象
*/
@Mapping(target = "registerDays", expression = "java(calculateRegisterDays(user))")
UserVO userToUserVO(User user);
/**
* 将注册DTO转换为PO
*
* @param dto 用户注册数据传输对象
* @return 用户持久化对象
*/
@Mapping(target = "password", ignore = true) // 密码需要特殊处理,不直接映射
@Mapping(target = "id", ignore = true)
@Mapping(target = "createTime", expression = "java(java.time.LocalDateTime.now())")
@Mapping(target = "updateTime", expression = "java(java.time.LocalDateTime.now())")
@Mapping(target = "status", constant = "1") // 默认状态:启用
User userRegisterDTOToUser(UserRegisterDTO dto);
/**
* 批量转换PO到VO
*
* @param users 用户持久化对象列表
* @return 用户视图对象列表
*/
List<UserVO> usersToUserVOs(List<User> users);
/**
* 将更新DTO应用到PO
*
* @param dto 用户更新数据传输对象
* @param user 待更新的用户持久化对象
* @return 更新后的用户持久化对象
*/
@Mapping(target = "id", ignore = true)
@Mapping(target = "username", ignore = true)
@Mapping(target = "password", ignore = true)
@Mapping(target = "createTime", ignore = true)
@Mapping(target = "updateTime", expression = "java(java.time.LocalDateTime.now())")
User updateUserFromDTO(UserUpdateDTO dto, @MappingTarget User user);
/**
* 计算用户注册天数
*
* @param user 用户持久化对象
* @return 注册天数
*/
default Long calculateRegisterDays(User user) {
if (user == null || user.getCreateTime() == null) {
return 0L;
}
return ChronoUnit.DAYS.between(user.getCreateTime(), LocalDateTime.now());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 9.4 对象模型选择策略
在企业级应用中选择合适的对象模型至关重要,以下是一些建议:
接口与前端交互:使用DTO接收请求,VO返回响应
/** * 用户控制器 * 使用DTO接收请求,VO返回响应 */ @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; /** * 用户注册 * * @param registerDTO 注册请求数据 * @return 注册结果 */ @PostMapping("/register") public Result<UserVO> register(@RequestBody @Valid UserRegisterDTO registerDTO) { UserVO userVO = userService.register(registerDTO); return Result.success(userVO); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23数据库交互:使用PO和DAO
/** * 用户仓库 * 使用JPA操作用户PO */ @Repository public interface UserRepository extends JpaRepository<User, Long> { /** * 根据用户名查询用户 * * @param username 用户名 * @return 用户对象 */ Optional<User> findByUsername(String username); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15业务逻辑处理:使用BO封装多个PO和业务规则
/** * 权限检查服务 * 使用UserBO进行权限校验 */ @Service public class AuthorizationService { /** * 检查用户是否有权限访问资源 * * @param userId 用户ID * @param resource 资源标识 * @return 是否有权限 */ public boolean hasPermission(Long userId, String resource) { // 获取用户业务对象 UserBO userBO = getUserBusinessObject(userId); // 检查权限 return userBO.hasPermission("ACCESS_" + resource); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22领域驱动设计:使用DO表达领域概念和规则
/** * 订单服务 * 使用OrderDO实现订单业务逻辑 */ @Service public class OrderService { /** * 创建订单 * * @param customerId 客户ID * @param items 订单项 * @return 创建的订单 */ @Transactional public OrderDO createOrder(Long customerId, List<OrderItemRequest> items) { // 使用领域服务创建订单领域对象 OrderDO order = orderDomainService.createOrder(customerId, items); // 执行领域操作 order.submit(); // 持久化 orderRepository.save(order); return order; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28微服务通信:使用TO
/** * 订单事件发布服务 * 使用TO在微服务间通信 */ @Service public class OrderEventPublisher { @Autowired private KafkaTemplate<String, String> kafkaTemplate; @Autowired private ObjectMapper objectMapper; /** * 发布订单创建事件 * * @param order 订单领域对象 */ public void publishOrderCreatedEvent(OrderDO order) { // 转换为传输对象 OrderTO orderTO = orderMapper.toTransferObject(order); try { // 发布事件 kafkaTemplate.send( "order-events", order.getId().toString(), objectMapper.writeValueAsString(orderTO) ); } catch (Exception e) { throw new EventPublishException("发布订单创建事件失败", e); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
编辑此页 (opens new window)
上次更新: 2025/03/17, 00:55:23