核心定義對比
| 概念 | 全稱 | 核心約束 | 典型場景 | 關係 |
| POJO | Plain Old Java Object | 無強制約束,普通類,通常有私有字段+公共 getter/setter | 數據傳輸對象 (DTO)、實體類、VO | 最寬泛的基類 |
| JavaBean | — | 1. public 無參構造器 2. 私有字段+標準 getter/setter 3. 實現 Serializable 4. 無 public 非靜態字段 | 可視化組件、配置對象、框架數據綁定 (Spring Bean、JSP、序列化) | 符合規範的 POJO 子集 |
| DAO | Data Access Object | 接口/類封裝數據訪問邏輯 (CRUD)、事務邊界、異常轉換 | 持久層:UserDao、OrderDao、MyBatis Mapper、JPA Repository | 職責角色,非數據結構 |
代碼形態對比
1. 標準 POJO (最常見)
public class User {
private Long id;
private String username;
private String email;
// getter/setter (可由 IDE/Lombok 生成)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
// ... 省略其他
}
2. 標準 JavaBean (符合規範)
import java.io.Serializable;
public class UserConfig implements Serializable { // 1. 實現序列化
private String theme; // 2. 私有字段
private int fontSize;
public UserConfig() {} // 3. public 無參構造器
// 4. 標準 getter/setter 命名
public String getTheme() { return theme; }
public void setTheme(String theme) { this.theme = theme; }
public int getFontSize() { return fontSize; }
public void setFontSize(int fontSize) { this.fontSize = fontSize; }
// boolean 類型 getter 可用 is 前綴
private boolean darkMode;
public boolean isDarkMode() { return darkMode; }
public void setDarkMode(boolean darkMode) { this.darkMode = darkMode; }
}
3. DAO 接口與實現
// 接口定義 (面向接口編程)
public interface UserDao {
User findById(Long id);
List findAll();
void insert(User user);
void update(User user);
void deleteById(Long id);
}
// MyBatis Mapper 實現 (XML 或註解)
@Mapper
public interface UserMapper extends UserDao {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO users(username, email) VALUES(#{username}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(User user);
// ...
}
// 手寫 JDBC 實現 (教學用)
public class UserDaoJdbcImpl implements UserDao {
private DataSource dataSource;
@Override
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return mapRow(rs);
}
} catch (SQLException e) {
throw new DataAccessException("查詢失敗", e);
}
return null;
}
// ...
}
分層架構中的角色定位
┌─────────────────────────────────────┐
│ Controller (VO) │ ← 接收/返回 View Object (常為 JavaBean)
├─────────────────────────────────────┤
│ Service (BO/DTO) │ ← 業務邏輯,操作 Business Object / Data Transfer Object
├─────────────────────────────────────┤
│ DAO / Mapper (PO/DO) │ ← 持久層,操作 Persistent Object / Domain Object (POJO)
├─────────────────────────────────────┤
│ Database (Table) │
└─────────────────────────────────────┘
- PO/Entity:與數據庫表映射的 POJO,通常加持久化註解 (`@Entity`, `@Table`)。
- DTO:服務層間、遠程調用傳輸的 POJO,防止暴露內部模型。
- VO:控制器返回前端的 POJO,針對視圖定制字段。
- BO:業務領域模型,封裝業務行為 (DDD 風格)。
常見誤區與最佳實踐
| 誤區 | 後果 | 正確做法 |
| 把 DAO 寫成單例類,裡面放 Connection | 線程不安全、連接洩漏、事務混亂 | DAO 無狀態,連接從 DataSource 獲取,配合連接池、事務管理器 |
| Entity 直接作為 Controller 返回值 | 暴露敏感字段 (密碼、鹽值)、循環引用導致序列化爆炸、前端耦合數據庫結構 | 轉換為 VO/DTO,使用 MapStruct/BeanUtils/手寫轉換 |
| JavaBean 必須實現 Serializable | 非分布式/非持久化場景強制序列化增加開銷 | 僅在跨進程傳輸 (RPC、Session 儲存、緩存) 時實現 |
| Getter/Setter 寫錯導致框架失效 | MyBatis/Jackson/Spring 綁定失敗 | 嚴格遵循 JavaBean 命名規範,用 Lombok @Data/@Getter/@Setter 保證一致性 |
Lombok 時代的最佳實踐
// POJO/Entity: 無參構造器 + 全參構造器 + getter/setter + toString/equals/hashCode
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id") // 僅用 id 比較,避免關聯對象循環
@TableName("users") // MyBatis-Plus
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
// VO: 針對前端定制,可能排除敏感字段、扁平化關聯對象
@Data
@NoArgsConstructor
public class UserVO {
private Long id;
private String username;
private String email; // 不返回 password
private String roleName; // 扁平化角色名
}
// DTO: 接收參數,校驗註解
@Data
public class UserDTO {
@NotBlank
@Size(max = 50)
private String username;
@Email
@NotBlank
private String email;
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-zA-Z]).{8,20}$")
private String password; // 僅接收,不返回
}
總結
- POJO 是統稱:「只是個普通 Java 對象」。
- JavaBean 是規範:「有無參構造、標準屬性訪問器、可序列化」。
- DAO 是職責:「封裝數據訪問細節,為上層提供領域語義接口」。
- 實際開發中,**Entity/DTO/VO 皆為 POJO**,視場景決定是否遵循 JavaBean 規範。**DAO 面向接口編程**,實現細節隔離在持久層框架 (MyBatis/JPA) 或手寫實現中。