acooly-guide

Acooly框架開發指南

簡介

Acooly框架是基於目前業界最常用的J2EE開發基礎技術棧開發的二次快速開發體系和框架。主要由框架核心,大量組件使用maven工程做集成,同時根據核心框架的開發規則和約定封裝,開發了自動代碼生成工具,提高開發效率。

主要的考慮模式和流程如下:

  1. 使用acooly-archetype建立工程
  2. 根據需求,添加對應的組件.
  3. 根據acooly框架設計規範和約定完成數據庫的設計
  4. 使用acooly-coder連接數據庫生成初始代碼
  5. 在工程中根據業務需求,調整生成的代碼,添加新的功能
  6. run,運行測試驗證~

技術架構

上圖是acooly框架的主線架構,採用開源的輕量級J2EE解決方案。

  • 主線技術棧:JDK8/spring boot/JPA/Mybatis/Spring4/jsp/freemarker

  • 服務採用:Dubbo(RPC)方案/CXF(SOAP)/HTTP-OpenApi開放平臺解決方案

設計規範和約定

acooly框架爲方便統一封裝,提高代碼質量和開發效率,我們在開放技術的基礎上,做了一些本領域範圍的規範和約定,基於本框架的開發需要遵循這些要求,才能更好的應用框架帶來的福利~

數據庫設計約定

爲了方便自動生成結構性代碼,對數據庫表結構設計進行部分約定,但都符合常規習慣。

  • 表名全部小寫,不能以數字開頭,必須添加備註
  • 每個表必須有以id命名的物理主鍵,且爲數字類型,如:mysql爲bigint, oracle爲number。
  • 列名稱全部小寫,不能以數字開頭;如果存在多個自然單詞的組合,使用下劃線分隔(_)。如:user_type
  • 列定義必須添加備註 (這個備註則爲生成的頁面及表單的label)
  • 每個表必須添加create_timeupdate_time兩個日期時間類型的字段,但無需手動管理 (在save/update時,框架會自己維護創建時間和最後修改時間)。
  • 如果有選項類型的字段,其選項值使用類json格式寫入列備註字段,自動生成工具會自動爲該列對應的屬性和頁面生成選項。如:表列爲:user_type ,備註可以爲:用戶類型 {normal:普通,vip:高級}

特別注意,強烈要求選項類(自動生成枚舉類的)字段項目全局唯一名稱,否則會生成enum名稱相同的枚舉相互覆蓋。

程序設計規範

  • 實體(Entity)必須繼承AbstractEntity,並且所有Annotation的標註採用屬性標註方式(如果@Id採用屬性則所有標註以屬性爲準)
  • 採用JPA時,DAO繼承EntityJpaDao,採用mybatis時,DAO繼承EntityMybatisDao

其他約定在後續章節中逐步介紹~

開發介紹

本章節對業務開發中,各層開發的基礎方式,重點,常規技巧和部分封裝進行詳細說明,以幫助使用者快速熟悉和應用框架能力。

Entity開發

Entity原則上是與數據表一一對應的,我們一般會通過實體做數據和對象的轉換對象,在Acooly框架中,我們採用Entity與Domain合併的方式進行構建和開發。下面是一個典型的實體類.

@Entity
@Table(name = "SYS_USER")
@JsonIgnoreProperties({ "password", "salt" })
public class User extends AbstractEntity {
    /** uid */
    private static final long serialVersionUID = 1L;
    /** 登陸名 */
    private String username;
    /** 密碼 */
    private String password;
    /** 鹽 */
    private String salt;
    /** 名字 */
    private String realName;

    /** 電子郵件 */
    private String email;
    /** 手機號碼 */
    private String mobileNo;
    // getter/setter
    // ...
}

有幾個需要注意的點:

  • 儘量不要使用jpa映射關係(一對一、一對多、多對多),很難做性能調優
  • jpa註解標註在屬性上的方案
  • 使用mybatis時,框架也需要jpa註解提供單表操作能力,並且會忽略映射關係
  • 必須繼承AbstractEntity對象
  • 可以使用transient關鍵字忽略屬性(此屬性不會映射爲數據庫字段)

DAO層開發

acooly框架的DAO層開發主要採用實體開發模式,及映射表爲對應的實體javabean對象,然後採用OR-Mapping或Mybatis封裝進行操作。下面分別介紹下,使用JPA,MyBatis和jdbcTemplate進行DAO層開發的方案。

acooly框架的DAO層封裝主要圍繞核心DAO接口EntityDao進行封裝,對下封裝實現通用的EntityDao實現,對上(服務層)提供統一接口的EntityDao實現。

com.acooly.core.common.dao.EntityDao

/**
 * 針對單個Entity對象的操作,不依賴於具體ORM實現方案.
 * 
 * @author zhangpu
 */
public interface EntityDao<T> {

    void create(T o);

    T get(Serializable id);

    void update(T o);

    T saveAndFlush(T entity);

    void flush();

    void saves(List<T> entities);

    void remove(T o);

    void removeById(Serializable id);

    void removes(Serializable... ids);

    List<T> getAll();

    List<T> find(String property, Object value);

    T findUniqu(String property, Object value);

    PageInfo<T> query(PageInfo<T> pageInfo, Map<String, Object> map, Map<String, Boolean> sortMap);

    List<T> list(Map<String, Object> map, Map<String, Boolean> sortMap);

}

JPA

封裝說明

JPA(Hibernate實現)是acooly框架封裝的默認DAO實現方式,也是最適合做管理類業務開發的DAO方案選擇,如果你選擇使用JPA方式做交易類開發,如果不是對hibernate特性特別瞭解,目前建議儘量減少關聯配置,多采用單表+字段冗餘的解決方案。

Acooly框架的DAO/JPA的封裝是基於EntityJpaDao接口的,它是EntityDao的子類,同時也是JpaRepository的子類,通過了Spring-Data的JPA封裝接口。

com.acooly.core.common.dao.jpa.EntityJpaDao

@NoRepositoryBean
public interface EntityJpaDao<T, ID extends Serializable> extends JpaRepository<T, ID>, EntityDao<T> {

}

Acooly對JPA的封裝是通過實現自定義的JpaRepositoryFactoryBean,爲EntityJapDao提供默認封裝實現,具體的實現類是:com.acooly.module.jpa.ex.AbstractEntityJpaDao

同時在動態查詢方面,我們通過約定查詢表單的命名規則,自動組裝QBC的查詢條件,實現自動化多條件,多排序分頁查詢。如:EQ_userNam表示:userName=該表單的值。請關注後面的詳細說明

DAO開發

本節以示例說明JAP的DAO的常規開發,Entity類以前面的User對象爲例。

框架自封裝的EntityDao,請直接採用如下格式什麼和建立你的DAO接口。

public interface UserDao extends EntityJpaDao<User, Long> {

}

OK,UserDao已經完成開發,具備了基本的CRUD,分頁查詢等基本功能,可以直接採用接口注入方式注入任何其他spring的javabean中使用。

問題1. 如果我有特別的查詢需求怎麼辦?比如我要根據用戶名查詢User?
你可以根據spring-data提供的JPA接口聲明方式提供靈活的自定義查詢解決。例如:

/** 根據用戶名等於方式查詢用戶,如果該屬性是唯一索引,則可以返還單個實體對象,否則返還List */
User findByUsername(String username);

/** 根據傳入的用戶名做like查詢 */
List<User> findByLikeUserName(String username);

/** 根據用戶名和狀態組合查詢 */
User findByUserNameAndStatus(String username,int status);

/** 根據用戶名或手機查詢用戶,結果採用Desc排序 */
List<User> findByUserNameOrMobileNoDesc(String username,String mobileNo);

以上代碼提供的是根據用戶名查詢User的DAO自動代理方法。原則是什麼!?

  • 所有的查詢都以findBy開頭。
  • 可以接多個接條件關鍵字(可選,無則表示=)+屬性名稱,多組查詢條件間採用關係關鍵字連接(Or 或 And)
  • 每個關鍵字,屬性,條件連接單詞間都採用駝峯格式
  • 每組查詢條件依次對應該方法的參數
  • 注意like條件是不會自動帶%的,需要在傳入值中傳入%,但是可以使用contaits代替。

具體的查詢語法和規則請參考:http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#jpa.query-methods.query-creation

問題2:我覺得還是不能滿足我的需求,比如:我需要傳入一個參數,然後匹配用戶名,郵箱和手機號碼,只要配置任何一個就返回!?
沒問題,我們需要採用@Query查詢方式定義查詢接口。如下:

/** 
* 採用@Query方式個任意命名的接口方法,定義一個查詢SQL,這裏是JPQL,然後通過?+參數序號(第一個參數爲1)的方式在SQL中注入參數值 
*/
@Query("from User where username = ?1 or email = ?1 or mobileNo = ?1")
User getAuthenticateUser(String key);

// 下面是一些其他實例

/** 如果我們是更新SQL(insert,update,delete),需要增加@Modifying */
@Modifying
@Query("update User set loginFailTimes = 0 where username = ?1")
void clearLoginFailureCount(String username);

/**
* 使用原生SQL查詢,注意,對於直接返回數字類型的SQL,JAP只能支持BigInteger,BigLong,BigDecimal等類型。
*/
@Query(value="select sum(t1.income) from anyOtherTableName t1 where t1.customerId = ?1",nativeQuery = true)
BigInteger getUserTotalIncome(Long id);

**問題3:熱,我現在想做個黑複雜的
查詢,也許是多個表關聯查詢返回個自定義結構,並不是某個實體怎麼辦?報表經常有這種需求吧?有時候爲了提高綜合性能和控制力度也需要吧?**

��~,你是對的,肯定會有這種需求!我們採用JPA擴展DAO方式解決,在擴展DAO中你可以使用任何持久化技術來擴展是你的DAO,如:jdbc或者mybatis。下面我們看下怎麼擴展UserDao

第一步:新建一個名稱爲UserCustomDao的接口,在該接口中聲明你需要的特別DAO接口。

public interface UserCustomDao {
    /** 自定義分頁查詢 */
    PageInfo<User> customeQuery(PageInfo<User> pageInfo, Map<String, String> map);

    /** 自定義查詢返回DTO對象 */
    List<UserDto> queryDto(Map<String, Object> map);
}

第二步:新增一個名稱爲UserDaoImpl的類實現UserCustomDao,下面以JdbcTemplate的方式來實現擴展Dao

public class UserDaoImpl extends AbstractJdbcTemplateDao implements UserCustomDao {

    @Override
    public PageInfo<User> customeQuery(PageInfo<User> pageInfo, Map<String, String> map) {
        String sql = "...";
        return super.query(pageInfo, sql, User.class);
    }

    @Override
    public List<UserDto> queryDto(Map<String, Object> map) {
        String sql = "select user_name as userName,password, user_type as userType from sys_user where 1= 1";
        //根據條件,動態組裝sql
        return super.queryForList(sql, UserDto.class);
    }

}

注意:

  • 擴展Dao實現類的命名規則必須爲:EntityDao接口名稱(本例爲UserDao)+ Impl,則爲:UserDaoImpl
  • 如果使用JDBC實現,實現類強烈建議繼承AbstractJdbcTemplateDao,該抽象類封裝了查詢和分頁查詢,並自適應適配MySQL和Oracle.
  • 該實現類,無需添加@Respository或其他Spring的

第三步:在主Dao接口上繼承擴展接口,對上整合爲一個Dao服務。

public interface UserDao extends EntityJpaDao<User, Long>, UserCustomDao {

}

OK,完成~

動態查詢

Acooly框架在JPA封裝中,對動態分頁查詢對了全棧封裝,要使用自動分頁查詢,需要從視圖層的表單命名上遵循移動的約定。

處理原理:通過對錶單的名稱命名做特定約定,由控制層手機參數後,一路透傳到JAP的動態查詢方法(query和list),由封裝方法通過命名約定自動解析和組裝多條件分頁查詢。特別的,自動多條件查詢

查詢條件表單命名規則:search_條件關鍵字_實體類屬性名稱

實例:

  • search_EQ_userName: 表示:userName=表單值
  • search_LLIKE_userName 表示 userName like ‘%表單值’
  • search_RLIKE_userName 表示 userName like ‘表單值%’
  • search_GT_startTime 表示 startTime > 表單值
  • search_GTE_startTime 表示 startTime >= 表單值
  • search_LT_startTime 表示 startTime < 表單值
  • search_LTE_startTime 表示 startTime <= 表單值

支持的條件關鍵字:EQ, NEQ, LIKE, LLIKE, RLIKE, GT, LT, GTE, LTE, IN, NOTIN, NULL, NOTNULL,

MyBatis

框架對mybatis的支持非常強大。可以不用寫一行sql,完成單表的增刪改查。

package com.acooly.showcase.demo.mapper;

import com.acooly.module.mybatis.EntityMybatisDao;
import com.acooly.showcase.demo.domain.Customer;

public interface CustomerMapper extends EntityMybatisDao<Customer> {

}

有些場景我們需要編寫sql,mapper文件定義到src/main/resources/mybatis/**/*Mapper.xml路徑。注意:新增mapper中的id不能和EntityMybatisDao接口中定義的方法明重複。

Jdbc

Jdbc的Dao實現主要是使用JdbcTemplate的Acooly擴展實現AbstractJdbcTemplateDao,在該類中,注入了標註的jdbcTempale實例,同時提供了自適應MySQL和Oracle的多條件,分頁查詢或集合查詢。

服務層開發

服務層以EntityService接口爲基礎,通過泛型自動識別和從spring容器注入對應的EntityDao,實現Dao層提供的CRUD和分頁查詢。

控制層開發

根據經驗,在有視圖的業務場景中,控制層的開發工作量其實相對比較大,所以我們考慮,爲了提高開發效率和代碼質量,在控制層做了大量的常用功能的“最佳實踐(相對來說)”的封裝。同時與服務層及一下封裝的功能提供無縫對接,提供基於實體的CRUD,分頁查詢,導入,導出等功能的統一封裝實現。

封裝說明

繼承體系

針對我們的目的,我們總體封裝的思路包括以下幾點:

  • 無縫對接後端服務層對實體的封裝功能。
  • 通過泛型實現實體及服務的識別和自動注入
  • 獨立封裝所有功能的模板方法,提供後端服務的控制車原則功能。
  • 對文件相關的操作,包括導入,導出(內存和自動分頁),上傳和下載統一封裝
  • 統一控制層的錯誤處理
  • 對JSON的返回提供統一的模型
  • 對通用視圖和特定視圖的控制流提供默認實現。

統一Json返回結構

如果你的控制層開發有選擇使用AJAX方式請求,要求直接返回數據,框架已在控制層做好對應的相關配置。強烈建議所有AJAX方法的返回對應都是統一封裝提供的JsonResult及其子類。

AJAX處理流程

視圖與控制層的交易模式,如果採用AJAX方式,上圖是框架中推薦的交互模式。

  1. 首先通過訪問控制器的視圖渲染方法,初始化視圖數據,然後返回瀏覽器顯示。
  2. 然後在通過瀏覽器端的組件或AJAX方法訪問控制器的數據獲取方法,拉取數據。
  3. 瀏覽器可以多次拉取數據後,渲染顯示數據。

視圖層開發

視圖層目前支持jsp/jstl和freemarker兩種視圖層語言開發,框架會自動根據springMVC的執行返回,搜索對應的視圖,採用freemarker優先,其次爲jsp方式匹配,一旦找到後,緩存並在整個進程生命週期有效。

視圖渲染

JSP/JSTL

標準JSTL和JSP規範開發支持,無需特別說明,對JSP做了常規安全處理,網上流行的安全問題都不會存在~

JSTL參考:http://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/

注意:由於servlet 3.0規範,jsp需要放到如下路徑:src/main/resources/META-INF/resources/WEB-INF/jsp

freemarker

不需要任何配置

注意:freemarker需要放到如下路徑:src/main/resources/templates

靜態資源

靜態資源放到如下路徑:src/main/resources/static

視圖開發

視圖開發請參見:Acooly框架視圖開發指南

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章