域对象(Domain Object)在Java Web开发中的应用 - 实践
域对象(Domain Object)在Java Web开发中的应用
什么是域对象?
域对象(Domain Object),也称为领域对象或实体对象,是面向对象编程中用于表示业务领域中的实体和概念的对象。它们封装了业务数据和行为,是业务模型的核心组成部分。
域对象的类型
在Java Web开发中,主要有四种类型的域对象:
- 普通JavaBean:简单的数据载体
- 实体类:与数据库表映射的对象
- 数据传输对象(DTO):在不同层之间传输数据的对象
- 值对象:表示不可变数据的对象
域对象的最佳实践
1. 基本域对象设计
以下是一个良好的域对象设计示例:
package com.xie.oa.domain;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 部门领域对象
* 代表业务领域中的部门实体
*/
public class Dept implements Serializable {
private static final long serialVersionUID = 1L;
// 基本属性
private Integer deptno; // 部门编号
private String dname; // 部门名称
private String loc; // 部门位置
// 审计字段
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createdBy;
private String updatedBy;
// 状态字段
private Boolean active;
// 构造方法
public Dept() {
this.active = true;
this.createTime = LocalDateTime.now();
}
public Dept(Integer deptno, String dname, String loc) {
this();
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
// Getter和Setter方法
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
// 业务方法
public void deactivate() {
this.active = false;
this.updateTime = LocalDateTime.now();
}
public void activate() {
this.active = true;
this.updateTime = LocalDateTime.now();
}
// 重写equals和hashCode方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dept dept = (Dept) o;
return Objects.equals(deptno, dept.deptno);
}
@Override
public int hashCode() {
return Objects.hash(deptno);
}
// 重写toString方法
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
", active=" + active +
'}';
}
// 验证方法
public boolean isValid() {
return deptno != null &&
dname != null && !dname.trim().isEmpty() &&
loc != null && !loc.trim().isEmpty();
}
// 构建器模式(可选)
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Integer deptno;
private String dname;
private String loc;
public Builder deptno(Integer deptno) {
this.deptno = deptno;
return this;
}
public Builder dname(String dname) {
this.dname = dname;
return this;
}
public Builder loc(String loc) {
this.loc = loc;
return this;
}
public Dept build() {
Dept dept = new Dept();
dept.setDeptno(deptno);
dept.setDname(dname);
dept.setLoc(loc);
return dept;
}
}
}
2. 使用域对象的Service层示例
package com.xie.oa.service;
import com.xie.oa.domain.Dept;
import java.util.List;
import java.util.Optional;
/**
* 部门服务接口
*/
public interface DeptService {
/**
* 根据ID查找部门
*/
Optional<Dept> findById(Integer deptno);
/**
* 查找所有部门
*/
List<Dept> findAll();
/**
* 查找所有活跃部门
*/
List<Dept> findAllActive();
/**
* 保存部门
*/
Dept save(Dept dept);
/**
* 更新部门
*/
Dept update(Dept dept);
/**
* 根据ID删除部门(逻辑删除)
*/
void deleteById(Integer deptno);
/**
* 检查部门名称是否已存在
*/
boolean existsByDname(String dname);
}
3. 服务实现
package com.xie.oa.service.impl;
import com.xie.oa.domain.Dept;
import com.xie.oa.repository.DeptRepository;
import com.xie.oa.service.DeptService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class DeptServiceImpl implements DeptService {
private final DeptRepository deptRepository;
public DeptServiceImpl(DeptRepository deptRepository) {
this.deptRepository = deptRepository;
}
@Override
@Transactional(readOnly = true)
public Optional<Dept> findById(Integer deptno) {
return deptRepository.findById(deptno);
}
@Override
@Transactional(readOnly = true)
public List<Dept> findAll() {
return deptRepository.findAll();
}
@Override
@Transactional(readOnly = true)
public List<Dept> findAllActive() {
return deptRepository.findByActiveTrue();
}
@Override
public Dept save(Dept dept) {
if (!dept.isValid()) {
throw new IllegalArgumentException("部门数据不完整");
}
if (existsByDname(dept.getDname())) {
throw new IllegalStateException("部门名称已存在");
}
return deptRepository.save(dept);
}
@Override
public Dept update(Dept dept) {
if (!dept.isValid()) {
throw new IllegalArgumentException("部门数据不完整");
}
return deptRepository.findById(dept.getDeptno())
.map(existingDept -> {
// 检查名称是否与其他部门冲突
if (!existingDept.getDname().equals(dept.getDname()) &&
existsByDname(dept.getDname())) {
throw new IllegalStateException("部门名称已存在");
}
existingDept.setDname(dept.getDname());
existingDept.setLoc(dept.getLoc());
existingDept.setUpdateTime(java.time.LocalDateTime.now());
return deptRepository.save(existingDept);
})
.orElseThrow(() -> new IllegalArgumentException("部门不存在"));
}
@Override
public void deleteById(Integer deptno) {
deptRepository.findById(deptno)
.ifPresent(dept -> {
dept.deactivate();
deptRepository.save(dept);
});
}
@Override
@Transactional(readOnly = true)
public boolean existsByDname(String dname) {
return deptRepository.existsByDnameAndActiveTrue(dname);
}
}
4. 使用构建器模式创建域对象
// 使用构建器模式创建Dept对象
Dept dept = Dept.builder()
.deptno(10)
.dname("技术部")
.loc("北京")
.build();
// 或者使用构造函数
Dept dept = new Dept(10, "技术部", "北京");
域对象的设计原则
- 高内聚低耦合:域对象应该包含相关的数据和行为,减少对外部依赖
- 封装性:隐藏内部实现细节,通过公共方法暴露功能
- 不可变性:在可能的情况下,使对象不可变以提高线程安全性
- 有效性:域对象应该能够自我验证其状态的有效性
- 业务逻辑:将属于该领域对象的业务逻辑放在域对象中
域对象在Web层中的使用
1. Controller中使用域对象
@RestController
@RequestMapping("/api/depts")
public class DeptController {
private final DeptService deptService;
public DeptController(DeptService deptService) {
this.deptService = deptService;
}
@GetMapping("/{deptno}")
public ResponseEntity<Dept> getDept(@PathVariable Integer deptno) {
return deptService.findById(deptno)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Dept> createDept(@Valid @RequestBody Dept dept) {
try {
Dept savedDept = deptService.save(dept);
return ResponseEntity.created(URI.create("/api/depts/" + savedDept.getDeptno()))
.body(savedDept);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (IllegalStateException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
@PutMapping("/{deptno}")
public ResponseEntity<Dept> updateDept(
@PathVariable Integer deptno,
@Valid @RequestBody Dept dept) {
if (!deptno.equals(dept.getDeptno())) {
return ResponseEntity.badRequest().build();
}
try {
Dept updatedDept = deptService.update(dept);
return ResponseEntity.ok(updatedDept);
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
} catch (IllegalStateException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
@DeleteMapping("/{deptno}")
public ResponseEntity<Void> deleteDept(@PathVariable Integer deptno) {
deptService.deleteById(deptno);
return ResponseEntity.noContent().build();
}
}
2. JSP页面中使用域对象
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
部门详情
部门详情
部门编号
${dept.deptno}
部门名称
${dept.dname}
部门位置
${dept.loc}
状态
${dept.active ? '活跃' : '已禁用'}
创建时间
${dept.createTime}
部门不存在
返回列表
域对象与数据转换
1. DTO模式
对于复杂的业务场景,可以使用DTO(Data Transfer Object)来优化数据传输:
/**
* 部门DTO,用于前后端数据传输
*/
public class DeptDTO {
private Integer deptno;
private String dname;
private String loc;
// 构造方法、Getter和Setter
/**
* 从领域对象转换为DTO
*/
public static DeptDTO fromDomain(Dept dept) {
DeptDTO dto = new DeptDTO();
dto.setDeptno(dept.getDeptno());
dto.setDname(dept.getDname());
dto.setLoc(dept.getLoc());
return dto;
}
/**
* 从DTO转换为领域对象
*/
public Dept toDomain() {
Dept dept = new Dept();
dept.setDeptno(this.deptno);
dept.setDname(this.dname);
dept.setLoc(this.loc);
return dept;
}
}
2. 使用MapStruct进行对象映射
对于大型项目,可以使用MapStruct等工具简化对象转换:
@Mapper(componentModel = "spring")
public interface DeptMapper {
DeptDTO deptToDeptDTO(Dept dept);
Dept deptDTOToDept(DeptDTO deptDTO);
}
总结
域对象是Java Web开发中的核心概念,良好的域对象设计可以提高代码的可维护性、可测试性和可扩展性。设计域对象时应遵循面向对象的原则,封装业务数据和行为,提供适当的验证方法,并考虑线程安全性和不可变性。
在实际项目中,域对象通常与Service层、Repository层和Web层协同工作,形成清晰的分层架构。通过合理使用DTO模式和对象映射工具,可以进一步优化系统性能和提高开发效率。
浙公网安备 33010602011771号