AppFuse 1.x 以 原型代碼生成 加速開發,Person 為示範實體,展示 Hibernate 映射、Spring DAO、單元測試整合。
Person.java@HibernateClass(table = "person")
public class Person extends BaseObject {
private Long id;
private String firstName;
private String lastName;
@HibernateId(column = "id", generator = "native")
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@HibernateProperty(column = "first_name", length = 50)
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
@HibernateProperty(column = "last_name", length = 50)
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
// equals/hashCode/toString 使用 Apache Commons Lang Builder
public boolean equals(Object o) {
if (!(o instanceof Person)) return false;
Person rhs = (Person) o;
return new EqualsBuilder()
.append(this.firstName, rhs.firstName)
.append(this.id, rhs.id)
.append(this.lastName, rhs.lastName)
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder(-1196181247, -1232855255)
.append(firstName).append(id).append(lastName).toHashCode();
}
public String toString() {
return new ToStringBuilder(this)
.append("lastName", lastName).append("id", id).append("firstName", firstName).toString();
}
}
註:原代碼使用 XDoclet 標籤 (@hibernate.class 等),現代專案改用 JPA 註解。
PersonDAO 介面:savePerson, getPerson, removePersonBaseDAOHibernate,注入 SessionFactory<bean id="personDAO" class="...PersonDAOHibernate"><property name="sessionFactory" ref="sessionFactory"/></bean>PersonDAOTest.javapublic class PersonDAOTest extends BaseDAOTestCase {
private PersonDAO dao;
private Person person;
protected void setUp() {
super.setUp();
dao = (PersonDAO) ctx.getBean("personDAO"); // ctx 為 Spring ApplicationContext
}
protected void tearDown() {
super.tearDown();
dao = null;
}
public void testGetPerson() {
person = new Person();
person.setFirstName("Matt");
person.setLastName("Raible");
dao.savePerson(person);
assertNotNull(person.getId()); // 生成器 native 產生 ID
person = dao.getPerson(person.getId());
assertEquals("Matt", person.getFirstName());
}
public void testSavePerson() {
person = dao.getPerson(1L);
person.setFirstName("Matt");
person.setLastName("Last Name Updated");
dao.savePerson(person);
assertEquals("Last Name Updated", person.getLastName());
}
public void testAddAndRemovePerson() {
person = new Person();
person.setFirstName("Bill");
person.setLastName("Joy");
dao.savePerson(person);
assertEquals("Bill", person.getFirstName());
assertNotNull(person.getId());
dao.removePerson(person.getId());
try {
person = dao.getPerson(person.getId());
fail("Person found in database");
} catch (DataAccessException dae) {
assertNotNull(dae); // 預期拋出異常
}
}
}
| 模式 | 體現 | 優勢 |
|---|---|---|
| BaseObject | 統一 equals/hashCode/toString 實現 | 減少樣板、一致性 |
| Commons Lang Builder | EqualsBuilder, HashCodeBuilder, ToStringBuilder | 避免手寫錯誤、支援反射/非反射模式 |
| BaseDAOTestCase | Spring Context 載入、事務邊界、資料清理 | 測試隔離、可重複執行 |
| Dependency Injection | ctx.getBean("personDAO") | 鬆耦合、易替換實現 |
| XDoclet → Annotation | 元數據從 JavaDoc 移至註解 | 類型安全、IDE 支援、編譯時檢查 |
// 領域模型
@Entity @Table(name = "person")
public class Person {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name", length = 50)
private String firstName;
@Column(name = "last_name", length = 50)
private String lastName;
// Lombok @Data 自動生成 equals/hashCode/toString/getter/setter
}
// Repository (Spring Data JPA)
public interface PersonRepository extends JpaRepository {
List findByLastName(String lastName);
}
// 測試
@DataJpaTest
class PersonRepositoryTest {
@Autowired PersonRepository repo;
@Test void crud() {
Person p = repo.save(new Person("Matt", "Raible"));
assertNotNull(p.getId());
assertEquals("Matt", repo.findById(p.getId()).get().getFirstName());
repo.deleteById(p.getId());
assertTrue(repo.findById(p.getId()).isEmpty());
}
}
AppFuse 1.7 的 Person 範例展示了 領域模型 → Hibernate 映射 → Spring DAO → 單元測試 的完整鏈路。其核心價值在於:標準化專案結構、基類消除樣板、測試優先驗證。現代框架 (Spring Data JPA, Lombok, JUnit 5) 進一步簡化代碼,但設計哲學一脈相承。