神奇好望角 The Magical Cape of Good Hope

          庸人不必自擾,智者何需千慮?
          posts - 26, comments - 50, trackbacks - 0, articles - 11
            BlogJava :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理

          最近閑來無事(樓主確實太懶了),重翻舊賬,搗鼓了下 JPA 2.0,通過不斷地寫代碼和谷歌,又有了一些舊瓶裝新酒的發(fā)現(xiàn)和吐槽。樓主將在這一系列文章中慢慢道來。本次開篇帶來的是兩個模板類:用作實體類基礎(chǔ)框架的 AbstractEntity, 以及實現(xiàn)了對實體的基本 CRUD 操作的 BasicEntityDao

          一個實體類必須實現(xiàn) java.io.Serializable 接口,必須有一個 ID 字段作為主鍵,且最好覆蓋 equalshashCode 方法。因為實體類和數(shù)據(jù)表有對應(yīng)關(guān)系,所以往往根據(jù) ID 來實現(xiàn) equalshashCode。這很自然地可以引出一個模板類,所有的實體類都可以從它繼承:

                  /**
                   * 該類可作為實體類的模板,其 {@link #equals(Object)} 和 {@link hashCode()} 方法基于主鍵實現(xiàn)。
                   * 子類只需要實現(xiàn) {@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 作為主鍵:

                  @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();
                      }
                  }
              

          繼續(xù)發(fā)揮想象,讓它支持樂觀鎖:

                  @MappedSuperclass
                  public class VersionedUuidEntity extends UuidEntity {
                      @Version
                      private int version;
                  }
              

          這兒順便插嘴吐槽下主鍵的類型。用整數(shù)還是 UUID 好呢?這個問題在網(wǎng)上也是爭論紛紛。在樓主看來,兩者各有優(yōu)劣:整數(shù)主鍵性能高,可讀性也好,但會對數(shù)據(jù)遷移,例如合并兩個數(shù)據(jù)庫,造成不小的麻煩,因為可能出現(xiàn)一大堆重復的主鍵;UUID 性能差些,看起來晃眼,雖然據(jù)說有些數(shù)據(jù)庫針對性地做了優(yōu)化,想來也不大可能優(yōu)于整數(shù),不過好處就是理論上出現(xiàn)重復主鍵的概率比中彩票還小(福彩除外)。說這么一大堆,其實還是蠻糾結(jié)啊……樓主一般傾向于用 UUID,只要服務(wù)器的配置夠勁,想來不會出現(xiàn)明顯的性能問題。

          接下來說說 BasicEntityDao,它提供了基本的 CRUD 實現(xiàn),可以用來為會話 Bean 做模板:

                  /**
                   * 提供了對實體進行基本 CRUD 操作的實現(xiàn),可作為會話 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";
                      }
          
                      /**
                       * 返回用于數(shù)據(jù)庫操作的 {@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() 的實現(xiàn)即可。假設(shè)樓主要做一個養(yǎng)雞場管理系統(tǒng),對雞圈進行操作的會話 Bean 就可以簡單地寫成:

                  @Stateless
                  public class CoopDao extends BasicEntityDao<Coop> {
                      @Persistence
                      private EntityManager em;
          
                      public CoopDao() {
                          super(Coop.class);
                      }
          
                      @Override
                      protected EntityManager getEntityManager() {
                          return em;
                      }
          
                      // 更多方法……
                  }
              

          評論

          # re: JPA 應(yīng)用技巧 1:介紹 AbstractEntity 和 BasicEntityDao  回復  更多評論   

          2011-09-07 19:54 by 來如風
          暈uuid還用這樣搞啊,有很多內(nèi)置的生成器可以了!!!!!!!

          # re: JPA 應(yīng)用技巧 1:介紹 AbstractEntity 和 BasicEntityDao  回復  更多評論   

          2011-09-07 20:47 by 蜀山兆孨龘
          @來如風
          先別忙著暈呵呵。我這兒討論的是 JPA 規(guī)范,而目前的最新版 2.0 不支持 UUID 生成,2.1 才有計劃。你一定是用的 Hibernate 之類的專有 API 吧?

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-09 08:22 by tb
          uuid還有這樣搞得嗎

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-09 09:49 by 蜀山兆孨龘
          @tb
          有什么質(zhì)疑請說出來,咱以理服人好不?

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-09 23:45 by 來如風
          樓主你看看spring的spring-data工程,你會發(fā)現(xiàn)dao怎么搞了,

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-10 00:27 by 蜀山兆孨龘
          @來如風
          我這兒只談 JPA,不涉及 Spring……數(shù)據(jù)庫訪問這種東西,封裝得越厚,性能降得越恐怖,不信你可以自己去測。功底夠好的話,完全可以直接寫 SQL 查詢語句(EntityManager#createNativeQuery),而只把封裝查詢結(jié)果和做緩存的任務(wù)交給 JPA。

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-10 22:23 by 來如風
          那研究jpa干什么,研究apache dbutil 或者ibatis 就夠了,前兩者完全可以滿足你的需求,在自己在業(yè)務(wù)層加個緩存,就夠了!!!!!!!!!!

          # re: JPA 應(yīng)用技巧 1:實體類和實體 DAO 模板  回復  更多評論   

          2011-09-11 19:56 by 蜀山兆孨龘
          @來如風
          既然 JPA 支持 EntityManager#createNativeQuery,為什么不能研究?用 JPA 就非得要和你說的那些框架一起用嗎?我這篇文章有什么問題請你直接指出來,不要亂扯別的框架好不好?
          主站蜘蛛池模板: 青田县| 平昌县| 天祝| 达拉特旗| 张掖市| 阜新市| 河池市| 灌阳县| 湖北省| 鄢陵县| 湖州市| 轮台县| 昆明市| 都昌县| 鄂伦春自治旗| 东宁县| 九龙城区| 乐业县| 鞍山市| 唐河县| 黄大仙区| 长岛县| 田林县| 深泽县| 淮安市| 巧家县| 泸定县| 高唐县| 巴林左旗| 汽车| 泽库县| 房山区| 泰来县| 仙游县| 红河县| 博罗县| 尉氏县| 惠安县| 普兰店市| 汉沽区| 晴隆县|