JPA 应用技巧 1:实体类和实体 DAO 模板

2 篇文章 0 订阅
 

JPA 应用技巧 1:实体类和实体 DAO 模板

Posted on 2011-09-07 17:40 蜀山兆孨龘 阅读(1350) 评论(8)   编辑   收藏 所属分类: Java EE

最近闲来无事(楼主确实太懒了),重翻旧账,捣鼓了下 JPA 2.0,通过不断地写代码和谷歌,又有了一些旧瓶装新酒的发现和吐槽。楼主将在这一系列文章中慢慢道来。本次开篇带来的是两个模板类:用作实体类基础框架的AbstractEntity, 以及实现了对实体的基本 CRUD 操作的 BasicEntityDao

一个实体类必须实现 java.io.Serializable 接口,必须有一个 ID 字段作为主键,且最好覆盖 equalshashCode 方法。因为实体类和数据表有对应关系,所以往往根据 ID 来实现 equalshashCode。这很自然地可以引出一个模板类,所有的实体类都可以从它继承:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
  * 该类可作为实体类的模板,其 {@link #equals(Object)} 和 {@link hashCode()} 方法基于主键实现。
  * 子类只需要实现 {@link #getId()} 方法。
  */
public abstract class AbstractEntity implements Serializable {
     /**
      * 返回主键。
      */
     public abstract Object getId();
 
     @Override
     public boolean equals(Object obj) {
         if ( this == obj) {
             return true ;
         }
         if (obj == null || getClass() != obj.getClass()) {
             return false ;
         }
         return getId() == null ? false
                 : getId().equals(((AbstractEntity) obj).getId());
     }
 
     @Override
     public int hashCode() {
         return Objects.hashCode(getId());
     }
}

针对主键的类型,AbstractEntity 可以进一步扩展。例如,可以扩展出一个 UuidEntity,它使用随机生成的 UUID 作为主键:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@MappedSuperclass
public class UuidEntity extends AbstractEntity {
     @Id
     private String id;
 
     @Override
     public String getId() {
         return id;
     }
 
     @PrePersist
     private void generateId() {
         // 仅在持久化前生成 ID,提升一点性能。
         id = UUID.randomUUID().toString();
     }
}

继续发挥想象,让它支持乐观锁:

?
1
2
3
4
5
@MappedSuperclass
public class VersionedUuidEntity extends UuidEntity {
     @Version
     private int version;
}

这儿顺便插嘴吐槽下主键的类型。用整数还是 UUID 好呢?这个问题在网上也是争论纷纷。在楼主看来,两者各有优劣:整数主键性能高,可读性也好,但会对数据迁移,例如合并两个数据库,造成不小的麻烦,因为可能出现一大堆重复的主键;UUID 性能差些,看起来晃眼,虽然据说有些数据库针对性地做了优化,想来也不大可能优于整数,不过好处就是理论上出现重复主键的概率比中彩票还小(福彩除外)。说这么一大堆,其实还是蛮纠结啊……楼主一般倾向于用 UUID,只要服务器的配置够劲,想来不会出现明显的性能问题。

接下来说说 BasicEntityDao,它提供了基本的 CRUD 实现,可以用来为会话 Bean 做模板:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
  * 提供了对实体进行基本 CRUD 操作的实现,可作为会话 Bean 的模板。
  */
public abstract class BasicEntityDao<T> {
     private Class<T> entityClass;
     private String entityClassName;
     private String findAllQuery;
     private String countQuery;
 
     protected BasicEntityDao(Class<T> entityClass) {
         this .entityClass = Objects.requireNonNull(entityClass);
         entityClassName = entityClass.getSimpleName();
         findAllQuery = "select e from " + entityClassName + " e" ;
         countQuery = "select count(e) from " + entityClassName + " e" ;
     }
 
     /**
      * 返回用于数据库操作的 {@link EntityManager} 实例。
      */
     protected abstract EntityManager getEntityManager();
 
     public void persist(T entity) {
         getEntityManager().persist(entity);
     }
 
     public T find(Object id) {
         return getEntityManager().find(entityClass, id);
     }
 
     public List<T> findAll() {
         return getEntityManager().createQuery(findAllQuery, entityClass).getResultList();
     }
 
     public List<T> findRange( int first, int max) {
         return getEntityManager().createQuery(findAllQuery, entityClass)
                 .setFirstResult(first).setMaxResults(max).getResultList();
     }
 
     public long count() {
         return (Long) getEntityManager().createQuery(countQuery).getSingleResult();
     }
 
     public T merge(T entity) {
         return getEntityManager().merge(entity);
     }
 
     public void remove(T entity) {
         getEntityManager().remove(merge(entity));
     }
}

子类只需要提供 getEntityManager() 的实现即可。假设楼主要做一个养鸡场管理系统,对鸡圈进行操作的会话 Bean 就可以简单地写成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Stateless
public class CoopDao extends BasicEntityDao<Coop> {
     @Persistence
     private EntityManager em;
 
     public CoopDao() {
         super (Coop. class );
     }
 
     @Override
     protected EntityManager getEntityManager() {
         return em;
     }
 
     // 更多方法……
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值