Java領(lǐng)域模型示例詳解
Java領(lǐng)域模型(POJO / Entity / VO/ DTO / BO)
一、為什么需要領(lǐng)域模型?
在Java開發(fā)中,數(shù)據(jù)在不同場景(如數(shù)據(jù)庫交互、前后端傳輸、前端展示、業(yè)務(wù)處理)中承擔(dān)的角色不同:
- 數(shù)據(jù)庫存儲需要與表結(jié)構(gòu)嚴(yán)格對應(yīng);
- 前后端傳輸需要精簡敏感信息;
- 前端展示需要格式化數(shù)據(jù);
- 業(yè)務(wù)處理需要組合多源數(shù)據(jù)。
領(lǐng)域模型的作用:通過“給數(shù)據(jù)貼標(biāo)簽”,明確數(shù)據(jù)在不同階段的職責(zé),避免混亂(如直接用數(shù)據(jù)庫實(shí)體返回給前端可能暴露密碼等敏感字段)。
二、核心領(lǐng)域模型對比總表
| 模型名稱 | 全稱 | 核心定位 | 應(yīng)用場景 | 數(shù)據(jù)流向 | 是否含業(yè)務(wù)邏輯 |
|---|---|---|---|---|---|
| POJO | Plain Old Java Object | 最基礎(chǔ)的Java實(shí)體,無任何約束 | 所有模型的“基類”概念 | 無特定流向 | 無 |
| Entity | 實(shí)體類 | 映射數(shù)據(jù)庫表結(jié)構(gòu)的“數(shù)據(jù)庫對象” | 與數(shù)據(jù)庫交互(ORM框架) | 數(shù)據(jù)庫↔后端(持久化) | 無 |
| DTO | Data Transfer Object | 封裝數(shù)據(jù)傳輸?shù)?ldquo;快遞盒” | 前后端交互、服務(wù)間調(diào)用(傳參/返回) | 前端→后端、服務(wù)→服務(wù) | 無 |
| VO | Value (View)Object | 封裝前端展示數(shù)據(jù)的“展示板” | 后端返回給前端的展示數(shù)據(jù) | 后端→前端(僅輸出) | 無 |
| BO | Business Object | 封裝業(yè)務(wù)邏輯的“業(yè)務(wù)載體” | 服務(wù)層(Service)內(nèi)部業(yè)務(wù)處理 | 后端內(nèi)部流轉(zhuǎn) | 有(業(yè)務(wù)計算) |
三、分模型詳解(附示例)
1. POJO(最基礎(chǔ)的“裸數(shù)據(jù)”)
- 全稱:Plain Old Java Object(普通老式Java對象)
- 核心定位:不依賴任何框架、無特殊約束的基礎(chǔ)Java類,僅包含屬性、get/set方法和構(gòu)造方法。
- 本質(zhì):所有領(lǐng)域模型的“最小單元”——Entity、DTO、VO等本質(zhì)上都是POJO的“特殊形態(tài)”(增加了場景約束)。
示例代碼:
// 純POJO,無任何注解/繼承,僅存數(shù)據(jù)
public class UserPojo {
private Long id;
private String name;
private Integer age;
// 必須有:無參構(gòu)造、get/set方法
public UserPojo() {}
// get/set省略
}關(guān)鍵特點(diǎn):
- 不繼承任何特定類(如
Serializable可選,非必須); - 不實(shí)現(xiàn)任何框架接口(如MyBatis的
BaseMapper); - 僅用于承載數(shù)據(jù),無業(yè)務(wù)邏輯方法。
2. Entity(數(shù)據(jù)庫的“鏡像”)
- 全稱:實(shí)體類(無縮寫,強(qiáng)調(diào)“與數(shù)據(jù)庫關(guān)聯(lián)”)
- 核心定位:與數(shù)據(jù)庫表一一映射的實(shí)體,是“數(shù)據(jù)庫數(shù)據(jù)在Java中的化身”。
- 核心場景:通過MyBatis、JPA等ORM框架操作數(shù)據(jù)庫(查詢、新增、修改)。
示例代碼:
// 與數(shù)據(jù)庫user表嚴(yán)格映射
@TableName("user") // MyBatis注解:指定對應(yīng)表名
public class UserEntity {
@TableId(type = IdType.AUTO) // 主鍵+自增策略
private Long id; // 對應(yīng)表中id字段(bigint類型)
@TableField("username") // 字段名與表一致(若相同可省略)
private String username; // 對應(yīng)表中username字段(varchar)
private String password; // 對應(yīng)表中password字段(敏感字段,需加密存儲)
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime; // 對應(yīng)表中create_time字段(datetime)
// 無參構(gòu)造、get/set方法
}關(guān)鍵特點(diǎn):
- 字段名、類型與數(shù)據(jù)庫表字段完全一致(含主鍵、索引、自增等配置);
- 帶有ORM框架注解(如
@TableName、@Id),明確與數(shù)據(jù)庫的映射關(guān)系; - 包含數(shù)據(jù)庫層面的屬性(如
createTime、updateTime),但無業(yè)務(wù)邏輯。
3. DTO(數(shù)據(jù)傳輸?shù)?ldquo;快遞盒”)
- 全稱:Data Transfer Object(數(shù)據(jù)傳輸對象)
- 核心定位:專門用于數(shù)據(jù)傳輸的載體,解決“傳輸效率”和“數(shù)據(jù)安全”問題。
- 核心場景:
- 前端→后端:接收前端提交的參數(shù)(如登錄、表單提交);
- 服務(wù)間調(diào)用:微服務(wù)A向微服務(wù)B傳遞數(shù)據(jù)(僅傳必要字段)。
示例1:接收前端登錄參數(shù)
// 前端登錄時傳遞的參數(shù)(僅包含必要字段)
public class LoginDTO {
@NotBlank(message = "用戶名不能為空") // 可加參數(shù)校驗(yàn)注解
private String username;
@NotBlank(message = "密碼不能為空")
private String password;
// get/set方法
}示例2:服務(wù)間傳輸用戶簡信息
// 微服務(wù)間傳輸?shù)挠脩魯?shù)據(jù)(屏蔽敏感字段)
public class UserSimpleDTO {
private Long id;
private String username; // 僅傳用戶名,不含password
private String role; // 僅傳必要的角色信息
// get/set方法
}關(guān)鍵特點(diǎn):
- 字段按需設(shè)計(僅包含傳輸所需的字段,避免冗余);
- 可包含參數(shù)校驗(yàn)注解(如
@NotBlank),用于前端傳參校驗(yàn); - 不包含數(shù)據(jù)庫相關(guān)字段(如
createTime),與數(shù)據(jù)庫解耦。
4. VO(前端展示的“展示板”)
- 全稱:Value Object(值對象)
- 核心定位:專門為前端展示準(zhǔn)備的數(shù)據(jù),是“后端給前端的最終數(shù)據(jù)包裝”。
- 核心場景:后端接口返回給前端的數(shù)據(jù)(如頁面列表、詳情頁展示)。
示例代碼:
// 前端用戶列表頁展示的數(shù)據(jù)(組合多表字段+格式化)
public class UserListVO {
private Long userId; // 前端習(xí)慣用userId,而非id
private String username; // 用戶名(來自UserEntity)
private String roleName; // 角色名稱(來自RoleEntity,UserEntity中無此字段)
private String createTimeStr; // 格式化后的時間(如"2024-05-01",前端直接展示)
// get/set方法
}關(guān)鍵特點(diǎn):
- 字段名可貼合前端習(xí)慣(如
userId而非id); - 可組合多個Entity的字段(如用戶信息+角色信息);
- 包含格式化后的字段(如日期轉(zhuǎn)字符串),減輕前端處理成本;
- 絕對不含敏感字段(如
password絕不會出現(xiàn)在VO中)。
5. BO(業(yè)務(wù)處理的“計算器”)
- 全稱:Business Object(業(yè)務(wù)對象)
- 核心定位:服務(wù)層(Service)內(nèi)部使用的“業(yè)務(wù)數(shù)據(jù)載體”,包含業(yè)務(wù)邏輯處理結(jié)果。
- 核心場景:復(fù)雜業(yè)務(wù)計算(如訂單金額計算、用戶積分統(tǒng)計)。
示例代碼:
// 訂單業(yè)務(wù)處理的BO(包含業(yè)務(wù)計算邏輯)
public class OrderBO {
// 基礎(chǔ)數(shù)據(jù)(來自Entity)
private OrderEntity order; // 訂單基本信息
private List<OrderItemEntity> items; // 訂單項(xiàng)列表
// 業(yè)務(wù)計算結(jié)果(非數(shù)據(jù)庫字段)
private BigDecimal totalAmount; // 總金額(計算:sum(單價×數(shù)量))
private BigDecimal discountAmount; // 折扣金額(根據(jù)會員等級計算)
private BigDecimal payAmount; // 實(shí)付金額(totalAmount - discountAmount)
// 業(yè)務(wù)方法:計算金額(包含核心業(yè)務(wù)邏輯)
public void calculateAmount(UserEntity user) {
// 1. 計算總金額
this.totalAmount = items.stream()
.map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 2. 計算折扣(會員等級越高,折扣越大)
this.discountAmount = calculateDiscount(user.getVipLevel(), totalAmount);
// 3. 計算實(shí)付金額
this.payAmount = totalAmount.subtract(discountAmount);
}
// 私有輔助方法(業(yè)務(wù)邏輯細(xì)節(jié))
private BigDecimal calculateDiscount(Integer vipLevel, BigDecimal total) {
if (vipLevel >= 3) return total.multiply(new BigDecimal("0.1")); // 10%折扣
if (vipLevel >= 2) return total.multiply(new BigDecimal("0.05")); // 5%折扣
return BigDecimal.ZERO;
}
// get/set方法
}關(guān)鍵特點(diǎn):
- 可組合多個Entity或DTO的數(shù)據(jù)(如訂單+訂單項(xiàng)+用戶信息);
- 包含業(yè)務(wù)計算結(jié)果(非數(shù)據(jù)庫存儲的臨時字段);
- 有業(yè)務(wù)邏輯方法(如
calculateAmount),是“業(yè)務(wù)邏輯的載體”; - 僅在服務(wù)層內(nèi)部使用,不暴露給前端或其他服務(wù)。
四、場景串聯(lián):一個流程看懂所有模型
以“用戶查詢訂單詳情”為例,看各模型如何配合:
- 數(shù)據(jù)庫查詢:通過
OrderEntity和OrderItemEntity從數(shù)據(jù)庫獲取原始訂單數(shù)據(jù); - 業(yè)務(wù)處理:在Service層用
OrderBO組合上述Entity,計算totalAmount、discountAmount等業(yè)務(wù)數(shù)據(jù); - 傳輸準(zhǔn)備:若需要調(diào)用“用戶服務(wù)”獲取會員信息,用
UserSimpleDTO傳輸精簡的用戶數(shù)據(jù); - 前端展示:將
OrderBO中的核心信息(過濾敏感字段、格式化日期)封裝到OrderDetailVO,返回給前端展示。
整個過程中,POJO是所有模型的基礎(chǔ)(上述模型均符合POJO的基本結(jié)構(gòu))。
五、核心原則總結(jié)
- 各司其職:
- 數(shù)據(jù)庫交互用Entity,傳輸數(shù)據(jù)用DTO,展示用VO,業(yè)務(wù)處理用BO,避免混用(如禁止用Entity直接返回給前端)。
- 按需設(shè)計:
- 簡單項(xiàng)目可簡化(如只用Entity+DTO),復(fù)雜項(xiàng)目必須嚴(yán)格區(qū)分(如電商系統(tǒng)的訂單模塊)。
- 轉(zhuǎn)換工具:
- 用MapStruct、BeanUtils等工具實(shí)現(xiàn)模型間轉(zhuǎn)換(如Entity→BO、BO→VO),減少重復(fù)代碼。
到此這篇關(guān)于Java領(lǐng)域模型示例詳解的文章就介紹到這了,更多相關(guān)java領(lǐng)域模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
Spring boot實(shí)現(xiàn)一個簡單的ioc(1)
這篇文章主要為大家詳細(xì)介紹了Spring boot實(shí)現(xiàn)一個簡單的ioc,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
使用logstash同步mysql數(shù)據(jù)到elasticsearch實(shí)現(xiàn)
這篇文章主要為大家介紹了使用logstash同步mysql數(shù)據(jù)到elasticsearch實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Java 8對LinkedHashSet元素進(jìn)行排序的操作方法
LinkedHashSet 是 Java 集合框架中的一個類,它繼承自 HashSet,并實(shí)現(xiàn)了 Set 接口,然而,LinkedHashSet 不支持元素的排序,它僅僅保持插入順序,所以本文給大家介紹了Java 8 如何對 LinkedHashSet 元素進(jìn)行排序,需要的朋友可以參考下2024-11-11
初學(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)
下面小編就為大家?guī)硪黄鯇W(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Spring MVC全局異常處理和單元測試_動力節(jié)點(diǎn)Java學(xué)院整理
本篇文章主要介紹了Spring MVC全局異常處理和單元測試,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08

