JPA (Hibernate、EclipseLink、OpenJPA 等) 雖簡化持久化,但實務專案仍需解決:事務邊界、延遲載入、實體移除、繼承映射等問題。本系列首篇建立**型別安全、可測試、框架無關**的泛型 DAO 基礎架構。
EntityManager 依賴,業務碼不直接呼叫 JPA API@Entity @Table(name="ORDERS")
public class Order {
@Id @GeneratedValue
private int id;
private String customerName;
private Date date;
// getters/setters
}
public interface Dao<K, E> {
void persist(E entity);
void remove(E entity);
E findById(K id);
List<E> findAll(); // 可選擴充
}
K = 主鍵型別,E = 實體型別。
public interface OrderDao extends Dao<Integer, Order> {
List<Order> findOrdersSubmittedSince(Date date);
}
public abstract class JpaDao<K, E> implements Dao<K, E> {
protected Class<E> entityClass;
@PersistenceContext
protected EntityManager entityManager;
@SuppressWarnings("unchecked")
public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[1];
}
public void persist(E entity) { entityManager.persist(entity); }
public void remove(E entity) { entityManager.remove(entity); }
public E findById(K id) { return entityManager.find(entityClass, id); }
public List<E> findAll() {
return entityManager.createQuery("SELECT e FROM " + entityClass.getName() + " e").getResultList();
}
}
getGenericSuperclass() 取得實際型別參數 E,避免子類重複指定EntityManager,支援 EJB3 / Spring (需 PersistenceAnnotationBeanPostProcessor)entityManager、entityClass 撰寫複雜查詢
public class JpaOrderDao extends JpaDao<Integer, Order> implements OrderDao {
public List<Order> findOrdersSubmittedSince(Date date) {
Query q = entityManager.createQuery(
"SELECT e FROM " + entityClass.getName() + " e WHERE e.date >= :date_since");
q.setParameter("date_since", date);
return q.getResultList();
}
}
@EJB(name="orderDao")
private OrderDao orderDao;
@Autowired
private OrderDao orderDao;
// 新增
Order o = new Order();
o.setCustomerName("Peter Johnson");
o.setDate(new Date());
orderDao.persist(o);
// 查詢
List<Order> orders = orderDao.findOrdersSubmittedSince(cutoffDate);
for (Order each : orders) {
System.out.println("order id = " + each.getId());
}
雙向關聯映射模式:@OneToMany/@ManyToOne 同步維護、mappedBy 陷阱、延遲載入最佳實踐。