[关闭]
@iamfox 2015-07-16T11:21:13.000000Z 字数 28543 阅读 1606

汇银新平台代码基本示例

汇银


1. 背景和改造思路

由于英迈思乐虎网交付的代码对持久层的封装原始且难用,进行查询需要编写大量的SQL拼接,关联查询还要依赖大量的数据库视图,对开发业务逻辑操作的效率和代码可维护性影响巨大,因此我们需要一套新的业务逻辑处理和持久化操作代码规范,通过对Hibernate进一步封装,即保留了Hibernate的开发效率高的特性,也要追求接近于Mybatis的灵活性和SQL性能。

以下以虚拟帐户模块做为演示:

虚拟帐户模块可用于管理平台内的各种可用虚拟货币和电子钱包,如红包、积分、返利余额等。

新建了三个maven工程如下:

工程 主要内容 依赖
hy-va-server 包含remote层、service、dao层 hy-va-pojo、lazypack-common
hy-va-domain 包含entity类 hy-va-pojo、lazypack-common
hy-va-pojo 包含远程调用的SOA服务接口,接口传递和返回的参数对象,各种javabean,自定义异常 lazypack-common

所有代码已提交至新的git服务器,地址是:http://192.168.19.21:8082/gitblit
开发人员可以用自己的帐号登录获取代码签出地址,帐号规则和默认密码同禅道。

2. 通用依赖

lazypack-common是一个自行开发的工具包,其最重要的功能就是围绕Hibernate封装了一套极简的操作框架,此外还有些常用工具类。

工具及基本封装类工程lazypack-common,jar包及源码包已上传至http://192.168.19.21:8081/nexus

  1. <dependency>
  2. <groupId>lazypack</groupId>
  3. <artifactId>lazypack-common</artifactId>
  4. <version>0.1</version>
  5. </dependency>

3. 必要的类介绍

看完了上面的效果,现在看一下要完成这样的自动化查询效果,都需要Service层以下提供哪些类。

3.1 业务实体

以帐户表(VA_VIRTUAL_ACCOUNT),汇银货币种类表(VA_HY_CURRENCY)为例。

两张表的关联关系是
VA_VIRTUAL_ACCOUNTVA_HY_CURRENCY多对一, 每个币种有多个帐户,每个帐户只会使用一个币种,外键是HY_CURRENCY_ID

以这两张表建立两个对应的主要根实体VirtualAccountHyCurrency

由于表字段过多,全放在一个实体类里会造成阅读和维护困难,因此HyCurrency实体进行了值对象的拆分,将字段分散到不同的组件对象里,多个对象映射一张表,如下图:
实体结构图
用户的字段被拆分到了VirtualAccountBaseInfoVirtualAccountBalanceVirtualAccountStatus几个对象中,所有字段映射全注解。与数据库的映射使用了Hibernate的Component机制。以下贴出两个实体类的代码,其余的类似:

3.1.1 VirtualAccount
  1. package com.lehumall.va.domain.entity.account;
  2. import java.util.Date;
  3. import javax.persistence.CascadeType;
  4. import javax.persistence.Entity;
  5. import javax.persistence.FetchType;
  6. import javax.persistence.JoinColumn;
  7. import javax.persistence.ManyToOne;
  8. import javax.persistence.Table;
  9. import org.hibernate.annotations.DynamicUpdate;
  10. import org.springframework.beans.BeanUtils;
  11. import com.lehumall.base.domain.entity.DomainLink;
  12. import com.lehumall.va.command.CreateVirtualAccountCommand;
  13. import com.lehumall.va.domain.entity.M;
  14. import com.lehumall.va.domain.entity.base.HyCurrency;
  15. import com.lehumall.va.dto.HyCurrencyDTO;
  16. import com.lehumall.va.dto.VirtualAccountDTO;
  17. import com.lehumall.va.exception.VirtualAccountException;
  18. import lazypack.common.component.StringIdBaseEntity;
  19. import lazypack.common.util.MoneyUtil;
  20. import lazypack.common.util.UUIDGenerator;
  21. /**
  22. * 虚拟帐户
  23. *
  24. * @author yuxiaoxiang
  25. *
  26. */
  27. @DynamicUpdate
  28. @Entity
  29. @Table(name = M.TABLE_PREFIX + "VIRTUAL_ACCOUNT")
  30. @SuppressWarnings("serial")
  31. public class VirtualAccount extends StringIdBaseEntity {
  32. /**
  33. * 帐户基本信息
  34. */
  35. private VirtualAccountBaseInfo baseInfo;
  36. /**
  37. * 帐户币种
  38. */
  39. @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
  40. @JoinColumn(name = "CURRENCY_ID", nullable = false)
  41. private HyCurrency currency;
  42. /**
  43. * 所属父帐户,父帐户余额由所有子帐户余额累加
  44. */
  45. @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
  46. @JoinColumn(name = "PARENT_ID", nullable = true)
  47. private VirtualAccount parent;
  48. /**
  49. * 帐户归属主体
  50. */
  51. private DomainLink onwer;
  52. /**
  53. * 帐户余额
  54. */
  55. private VirtualAccountBalance balance;
  56. /**
  57. * 帐户状态
  58. */
  59. private VirtualAccountStatus status;
  60. /**
  61. * 创建
  62. *
  63. * @param command
  64. */
  65. public void create(CreateVirtualAccountCommand command,
  66. HyCurrency hyCurrency, VirtualAccount parentAccount) {
  67. setId(UUIDGenerator.getUUID());
  68. setBaseInfo(new VirtualAccountBaseInfo());
  69. getBaseInfo().setName(command.getName());
  70. getBaseInfo().setInvalidDate(command.getInvalidDate());
  71. getBaseInfo().setProxyAccount(hyCurrency.getProxyCurrency());
  72. getBaseInfo().setCreateDate(new Date());
  73. setOnwer(new DomainLink());
  74. getOnwer().setOutId(command.getSubjectId());
  75. getOnwer().setType(command.getSubjectType());
  76. getOnwer().setOutName(command.getSubjectName());
  77. setCurrency(hyCurrency);
  78. setBalance(new VirtualAccountBalance());
  79. getBalance().setTotalAmount(0D);
  80. getBalance().setAvaiableAmount(0D);
  81. getBalance().setFrozenAmount(0d);
  82. setParent(parentAccount);
  83. setStatus(new VirtualAccountStatus());
  84. getStatus().setEnable(true);
  85. getStatus().setClose(false);
  86. }
  87. /**
  88. * 入帐
  89. *
  90. * @param amount
  91. * @throws VirtualAccountException
  92. */
  93. public void moneyIn(Double amount) throws VirtualAccountException {
  94. checkEnable();
  95. // MoneyUtil提供的计算方法为将Double转成BigDecimal计算,保证精度
  96. Double totalAmount = MoneyUtil.add(getBalance().getTotalAmount(),
  97. amount);
  98. Double avaiableAmount = MoneyUtil.add(getBalance().getAvaiableAmount(),
  99. amount);
  100. getBalance().setAvaiableAmount(avaiableAmount);
  101. getBalance().setTotalAmount(totalAmount);
  102. }
  103. /**
  104. * 重置帐户为0
  105. */
  106. public void reset() {
  107. getBalance().setAvaiableAmount(0D);
  108. getBalance().setTotalAmount(0D);
  109. getBalance().setFrozenAmount(0D);
  110. }
  111. /**
  112. * 从可用余额出帐
  113. *
  114. * @param amount
  115. * @throws VirtualAccountException
  116. */
  117. public void moneyOutFromAvaiable(Double amount)
  118. throws VirtualAccountException {
  119. checkEnable();
  120. if (getBalance().getAvaiableAmount() < amount) {
  121. throw new VirtualAccountException(
  122. VirtualAccountException.AVAIABLE_AMOUNT_NOT_ENOUTH,
  123. "可用余额不足");
  124. }
  125. Double totalAmount = MoneyUtil.sub(getBalance().getTotalAmount(),
  126. amount);
  127. Double avaiableAmount = MoneyUtil.sub(getBalance().getAvaiableAmount(),
  128. amount);
  129. getBalance().setAvaiableAmount(avaiableAmount);
  130. getBalance().setTotalAmount(totalAmount);
  131. }
  132. /**
  133. * 从冻结金额出帐
  134. *
  135. * @param amount
  136. * @throws VirtualAccountException
  137. */
  138. public void moneyOutFromFrozen(Double amount,
  139. VirtualAccount frozenVirtualAccount) throws VirtualAccountException {
  140. if (amount.doubleValue() != frozenVirtualAccount.getBalance()
  141. .getTotalAmount().doubleValue()) {
  142. throw new VirtualAccountException(
  143. VirtualAccountException.FROZEN_AMOUNT_WRONG, "冻结金额与出帐金额不等");
  144. }
  145. Double totalAmount = MoneyUtil.sub(getBalance().getTotalAmount(),
  146. amount);
  147. Double frozenAmount = MoneyUtil.sub(getBalance().getFrozenAmount(),
  148. amount);
  149. getBalance().setTotalAmount(totalAmount);
  150. getBalance().setFrozenAmount(frozenAmount);
  151. }
  152. /**
  153. * 冻结余额
  154. *
  155. * @param amount
  156. * @throws VirtualAccountException
  157. */
  158. public void moneyFrozen(Double amount) throws VirtualAccountException {
  159. checkEnable();
  160. if (getBalance().getAvaiableAmount().doubleValue() < amount) {
  161. throw new VirtualAccountException(
  162. VirtualAccountException.AVAIABLE_AMOUNT_NOT_ENOUTH,
  163. "可用余额不足");
  164. }
  165. Double frozenAmount = MoneyUtil.add(getBalance().getFrozenAmount(),
  166. amount);
  167. Double avaiableAmount = MoneyUtil.sub(getBalance().getAvaiableAmount(),
  168. amount);
  169. getBalance().setAvaiableAmount(avaiableAmount);
  170. getBalance().setFrozenAmount(frozenAmount);
  171. }
  172. /**
  173. * 解冻余额
  174. *
  175. * @param amount
  176. * @throws VirtualAccountException
  177. */
  178. public void moneyUnFreeze(Double amount) throws VirtualAccountException {
  179. checkEnable();
  180. if (getBalance().getAvaiableAmount().doubleValue() < amount) {
  181. throw new VirtualAccountException(
  182. VirtualAccountException.AVAIABLE_AMOUNT_NOT_ENOUTH,
  183. "可用余额不足");
  184. }
  185. Double frozenAmount = MoneyUtil.sub(getBalance().getFrozenAmount(),
  186. amount);
  187. Double avaiableAmount = MoneyUtil.add(getBalance().getAvaiableAmount(),
  188. amount);
  189. getBalance().setAvaiableAmount(avaiableAmount);
  190. getBalance().setFrozenAmount(frozenAmount);
  191. }
  192. /**
  193. * 检查帐户正常状态
  194. *
  195. * @throws VirtualAccountException
  196. */
  197. public void checkEnable() throws VirtualAccountException {
  198. if (getStatus().getClose()) {
  199. throw new VirtualAccountException(
  200. VirtualAccountException.ACCOUNT_CLOSE, "帐户已关闭");
  201. }
  202. if (!getStatus().getEnable()) {
  203. throw new VirtualAccountException(
  204. VirtualAccountException.ACCOUNT_FORBIDDEN, "帐户禁用中");
  205. }
  206. }
  207. /**
  208. * 启用
  209. */
  210. public void enable() {
  211. getStatus().setEnable(true);
  212. }
  213. /**
  214. * 禁用
  215. */
  216. public void disable() {
  217. getStatus().setEnable(false);
  218. }
  219. /**
  220. * 关闭
  221. */
  222. public void close() {
  223. getStatus().setClose(true);
  224. }
  225. /**
  226. * 将实体转换成返回给控制层和视图层使用的POJO
  227. */
  228. public VirtualAccountDTO trans2DTO(boolean transCurrency,
  229. boolean transParent) {
  230. VirtualAccountDTO dto = new VirtualAccountDTO();
  231. String[] ignore = new String[6];
  232. ignore[0] = "baseInfo";
  233. ignore[1] = "currency";
  234. ignore[2] = "parent";
  235. ignore[3] = "onwer";
  236. ignore[4] = "balance";
  237. ignore[5] = "status";
  238. BeanUtils.copyProperties(this, dto, ignore);
  239. dto.setBalance(getBalance().trans2DTO());
  240. dto.setBaseInfo(getBaseInfo().trans2DTO());
  241. dto.setOnwer(getOnwer().trans2DTO());
  242. dto.setStatus(getStatus().trans2DTO());
  243. if (transParent) {
  244. VirtualAccountDTO parentDto = getParent().trans2DTO(transCurrency,
  245. transParent);
  246. dto.setParent(parentDto);
  247. }
  248. if (transCurrency) {
  249. HyCurrencyDTO hyCurrencyDTO = getCurrency().trans2DTO();
  250. dto.setCurrency(hyCurrencyDTO);
  251. }
  252. return dto;
  253. }
  254. // setter/getter省略
  255. }
3.1.2 VirtualAccountBalance
  1. package com.lehumall.va.domain.entity.account;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. import org.springframework.beans.BeanUtils;
  5. import com.lehumall.base.domain.entity.M;
  6. import com.lehumall.va.dto.VirtualAccountBalanceDTO;
  7. /**
  8. * 帐户余额
  9. *
  10. * @author yuxiaoxiang
  11. *
  12. */
  13. @Embeddable
  14. public class VirtualAccountBalance {
  15. /**
  16. * 总金额
  17. */
  18. @Column(name = "TOTAL_AMOUNT", columnDefinition = M.MONEY_COLUMN)
  19. private Double totalAmount;
  20. /**
  21. * 可用金额
  22. */
  23. @Column(name = "AVAIABLE_AMOUNT", columnDefinition = M.MONEY_COLUMN)
  24. private Double avaiableAmount;
  25. /**
  26. * 冻结金额
  27. */
  28. @Column(name = "FROZEN_AMOUNT", columnDefinition = M.MONEY_COLUMN)
  29. private Double frozenAmount;
  30. public VirtualAccountBalanceDTO trans2DTO() {
  31. VirtualAccountBalanceDTO dto = new VirtualAccountBalanceDTO();
  32. BeanUtils.copyProperties(this, dto);
  33. return dto;
  34. }
  35. }
3.1.3 VirtualAccountBaseInfo
  1. package com.lehumall.va.domain.entity.account;
  2. import java.util.Date;
  3. import javax.persistence.Column;
  4. import javax.persistence.Embeddable;
  5. import org.hibernate.annotations.Type;
  6. import org.springframework.beans.BeanUtils;
  7. import com.lehumall.base.domain.entity.M;
  8. import com.lehumall.va.dto.VirtualAccountBaseInfoDTO;
  9. /**
  10. * 虚拟帐户基本信息
  11. *
  12. * @author yuxiaoxiang
  13. *
  14. */
  15. @Embeddable
  16. public class VirtualAccountBaseInfo {
  17. /**
  18. * 帐号名称
  19. */
  20. @Column(name = "NAME")
  21. private String name;
  22. /**
  23. * 创建时间
  24. */
  25. @Column(name = "CREATE_DATE", columnDefinition = M.DATE_COLUMN)
  26. private Date createDate;
  27. /**
  28. * 失效时间
  29. */
  30. @Column(name = "INVALID_DATE")
  31. private Date invalidDate;
  32. /**
  33. * 是否是代理帐户
  34. */
  35. @Type(type = "yes_no")
  36. @Column(name = "PROXY_ACCOUNT")
  37. private Boolean proxyAccount;
  38. public VirtualAccountBaseInfoDTO trans2DTO() {
  39. VirtualAccountBaseInfoDTO dto = new VirtualAccountBaseInfoDTO();
  40. BeanUtils.copyProperties(this, dto);
  41. return dto;
  42. }
  43. }
3.1.4 VirtualAccountStatus
  1. package com.lehumall.va.domain.entity.account;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. import org.hibernate.annotations.Type;
  5. import org.springframework.beans.BeanUtils;
  6. import com.lehumall.va.dto.VirtualAccountStatusDTO;
  7. /**
  8. * 帐户状态
  9. *
  10. * @author yuxiaoxiang
  11. *
  12. */
  13. @Embeddable
  14. public class VirtualAccountStatus {
  15. /**
  16. * 是否已启用
  17. */
  18. @Type(type = "yes_no")
  19. @Column(name = "ENABLE")
  20. private Boolean enable;
  21. /**
  22. * 是否已使用完关闭,不会再启用
  23. */
  24. @Type(type = "yes_no")
  25. @Column(name = "CLOSE")
  26. private Boolean close;
  27. public VirtualAccountStatusDTO trans2DTO() {
  28. VirtualAccountStatusDTO dto = new VirtualAccountStatusDTO();
  29. BeanUtils.copyProperties(this, dto);
  30. return dto;
  31. }
  32. public Boolean getEnable() {
  33. return enable;
  34. }
  35. public void setEnable(Boolean enable) {
  36. this.enable = enable;
  37. }
  38. public Boolean getClose() {
  39. return close;
  40. }
  41. public void setClose(Boolean close) {
  42. this.close = close;
  43. }
  44. }
3.1.5 HyCurrency
  1. package com.lehumall.va.domain.entity.base;
  2. import javax.persistence.Column;
  3. import javax.persistence.Entity;
  4. import javax.persistence.Table;
  5. import org.hibernate.annotations.DynamicUpdate;
  6. import org.hibernate.annotations.Type;
  7. import org.springframework.beans.BeanUtils;
  8. import com.lehumall.va.domain.entity.M;
  9. import com.lehumall.va.dto.HyCurrencyDTO;
  10. import lazypack.common.component.StringIdBaseEntity;
  11. /**
  12. * 汇银虚拟币种
  13. *
  14. * @author yuxiaoxiang
  15. *
  16. */
  17. @DynamicUpdate
  18. @Entity
  19. @Table(name = M.TABLE_PREFIX + "HY_CURRENCY")
  20. @SuppressWarnings("serial")
  21. public class HyCurrency extends StringIdBaseEntity {
  22. /**
  23. * 名称
  24. */
  25. @Column(name = "NAME")
  26. private String name;
  27. /**
  28. * 是否启用
  29. */
  30. @Type(type = "yes_no")
  31. @Column(name = "ENABLE")
  32. private Boolean enable;
  33. /**
  34. * 基准汇率,多少抵1元人民币
  35. */
  36. @Column(name = "EXCHANGE_ONE_YUAN", columnDefinition = M.MONEY_COLUMN)
  37. private Double exchangeOneYuan;
  38. /**
  39. * 单位名称
  40. */
  41. @Column(name = "UNIT")
  42. private String unit;
  43. /**
  44. * 是否是代理币种
  45. */
  46. @Type(type = "yes_no")
  47. @Column(name = "PROXY_CURRENCY")
  48. private Boolean proxyCurrency;
  49. public HyCurrencyDTO trans2DTO() {
  50. HyCurrencyDTO dto = new HyCurrencyDTO();
  51. BeanUtils.copyProperties(this, dto);
  52. return dto;
  53. }
  54. }

3.2 查询条件类QO

为了能从这些表里做查询,我们要把所有可用的查询条件封装到一个查询条件对象里,以方便框架能自动解析并生成SQL语句。以下列出其中两个类,其中的条件字段可以不断增加。

注:加在查询条件字段上的是一组自定义注解,有了这些注解,框架会自动解析并生成SQL,不需要再手工拼接SQL、HQL或者Criteria条件,Service和Dao也不需要自己写任何的查询方法。QO的具体用法会在后面JUNIT例子中演示。

3.2.1 VirtualAccountQO
  1. package com.lehumall.va.qo;
  2. import lazypack.common.annotation.QueryCondition;
  3. import lazypack.common.annotation.QueryConditionType;
  4. import lazypack.common.component.BaseQO;
  5. /**
  6. * 虚拟帐户查询条件
  7. *
  8. * @author yuxiaoxiang
  9. *
  10. */
  11. @SuppressWarnings("serial")
  12. public class VirtualAccountQO extends BaseQO<String> {
  13. private HyCurrencyQO hyCurrencyQO;
  14. @QueryCondition(name = "currency", type = QueryConditionType.FETCH_EAGER)
  15. private Boolean fetchCurrency = false;
  16. private VirtualAccountQO parentQO;
  17. @QueryCondition(name = "parent", type = QueryConditionType.FETCH_EAGER)
  18. private Boolean fetchParent = false;
  19. /**
  20. * 帐户归属者ID
  21. */
  22. @QueryCondition(name = "onwer.outId")
  23. private String subjectId;
  24. /**
  25. * 帐户归属者类型
  26. */
  27. @QueryCondition(name = "onwer.type")
  28. private Integer subjectType;
  29. public final static Integer ONWER_TYPE_USER = 1; // 个人用户
  30. public final static Integer ONWER_TYPE_MERCHANT = 2; // 商户
  31. public final static Integer ONWER_TYPE_PLATFORM = 3; // 平台
  32. /**
  33. * 是否已启用
  34. */
  35. @QueryCondition(name = "status.enable")
  36. private Boolean enable;
  37. /**
  38. * 是否已使用完关闭,不会再启用
  39. */
  40. @QueryCondition(name = "status.enable")
  41. private Boolean close;
  42. /**
  43. * 最小总余额
  44. */
  45. @QueryCondition(name = "totalAmount", type = QueryConditionType.GE)
  46. private Double geTotalAmount;
  47. /**
  48. * 最大总余额
  49. */
  50. @QueryCondition(name = "totalAmount", type = QueryConditionType.LE)
  51. private Double leTotalAmount;
  52. /**
  53. * 最小可用余额
  54. */
  55. @QueryCondition(name = "avaiableAmount", type = QueryConditionType.GE)
  56. private Double geAvaiableAmount;
  57. /**
  58. * 最大可用余额
  59. */
  60. @QueryCondition(name = "avaiableAmount", type = QueryConditionType.LE)
  61. private Double leAvaiableAmount;
  62. }

注:没有QueryConditionType属性的注解,都是默认为and组装和=判断的条件。框架也有提供or条件的注解,但or条件的组装注解可能不具有通用性,在某些情况下是or,在另一些情况下可能会用and,因此建议在dao的条件组装通用方法中进行or的处理。or条件组装的注解如下:

  1. /**
  2. * 是否已启用
  3. */
  4. @QueryCondition(name = "status.enable")
  5. @QueryConditionGroup("group1")
  6. private Boolean enable;
  7. /**
  8. * 是否已使用完关闭,不会再启用
  9. */
  10. @QueryCondition(name = "status.enable")
  11. @QueryConditionGroup("group1")
  12. private Boolean close;

同在一个QueryConditionGroup中的这两个查询条件,具有and (enable = xxx or close = xxx)的效果

3.2.2 HyCurrencyQO
  1. package com.lehumall.va.qo;
  2. import lazypack.common.annotation.QueryCondition;
  3. import lazypack.common.annotation.QueryConditionType;
  4. import lazypack.common.component.BaseQO;
  5. /**
  6. * 币种查询条件
  7. *
  8. * @author yuxiaoxiang
  9. *
  10. */
  11. @SuppressWarnings("serial")
  12. public class HyCurrencyQO extends BaseQO<String> {
  13. @QueryCondition(name = "useType", type = QueryConditionType.FETCH_EAGER)
  14. private boolean fetchUseType;
  15. @QueryCondition(name = "specImageMap", type = QueryConditionType.FETCH_EAGER)
  16. private boolean fetchSpecImageMap;
  17. }

3.3 操作命令类Command

Service类中的方法只接受两种参数,一种是QO,一种是CommandQO是查询条件对象,用于查询方法,Command是命令对象,用于增删改方法,它们有不同的父类,父类中定义了一些通用字段。

以下列举了部分命令和处理代码:

3.3.1 创建虚拟帐户
  1. package com.lehumall.va.command;
  2. import java.util.Date;
  3. import lazypack.common.component.BaseCommand;
  4. /**
  5. * 创建虚拟帐户
  6. *
  7. * @author yuxiaoxiang
  8. *
  9. */
  10. @SuppressWarnings("serial")
  11. public class CreateVirtualAccountCommand extends BaseCommand {
  12. /**
  13. * 币种
  14. */
  15. private String hyCurrencyId;
  16. /**
  17. * 帐号名称
  18. */
  19. private String name;
  20. /**
  21. * 失效时间
  22. */
  23. private Date invalidDate;
  24. /**
  25. * 父帐号
  26. */
  27. private String parentId;
  28. /**
  29. * 帐号归属者名称
  30. */
  31. private String onwerName;
  32. /**
  33. * 帐号归属者ID
  34. */
  35. private String onwerId;
  36. /**
  37. * 帐号归属者类型
  38. */
  39. private Integer onwerType;
  40. public final static Integer TYPE_USER = 1; // 个人用户
  41. public final static Integer TYPE_MERCHANT = 2; // 商户
  42. public final static Integer TYPE_PLATFORM = 3; // 平台
  43. }
  1. @Service
  2. @Transactional
  3. public class VirtualAccountServiceImpl extends
  4. BaseServiceImpl<VirtualAccount, VirtualAccountQO, VirtualAccountDao> {
  5. @Autowired
  6. private VirtualAccountDao virtualAccountDao;
  7. /**
  8. * 创建虚拟帐户
  9. *
  10. * @param command
  11. * @return
  12. * @throws VirtualAccountException
  13. */
  14. @Transactional(rollbackFor = VirtualAccountException.class)
  15. public VirtualAccount createVirtualAccount(
  16. CreateVirtualAccountCommand command) throws VirtualAccountException {
  17. HyCurrency currency = hyCurrencyDao.get(command.getHyCurrencyId());
  18. VirtualAccount parentAccount = null;
  19. if (command.getParentId() != null) {
  20. parentAccount = virtualAccountDao.load(command.getParentId());
  21. }
  22. VirtualAccount virtualAccount = new VirtualAccount();
  23. virtualAccount.create(command, currency, parentAccount);
  24. virtualAccountDao.save(virtualAccount);
  25. return virtualAccount;
  26. }
  27. }
3.3.2 入帐
  1. package com.lehumall.va.command;
  2. import lazypack.common.component.BaseCommand;
  3. /**
  4. * 入帐
  5. *
  6. * @author yuxiaoxiang
  7. *
  8. */
  9. @SuppressWarnings("serial")
  10. public class MoneyInCommand extends BaseCommand {
  11. /**
  12. * 帐户id
  13. */
  14. private String accountId;
  15. /**
  16. * 入帐的金额
  17. */
  18. private Double amount;
  19. }
  1. @Service
  2. @Transactional
  3. public class VirtualAccountServiceImpl extends
  4. BaseServiceImpl<VirtualAccount, VirtualAccountQO, VirtualAccountDao> {
  5. @Autowired
  6. private VirtualAccountDao virtualAccountDao;
  7. /**
  8. * 入帐
  9. *
  10. * @param command
  11. * @return
  12. * @throws VirtualAccountException
  13. */
  14. public VirtualAccountBalance moneyIn(MoneyInCommand command)
  15. throws VirtualAccountException {
  16. VirtualAccount virtualAccount = virtualAccountDao.get(command
  17. .getAccountId());
  18. if (!virtualAccount.getStatus().getEnable()) {
  19. throw new VirtualAccountException(
  20. VirtualAccountException.ACCOUNT_FORBIDDEN, "帐户禁用中");
  21. }
  22. if (virtualAccount.getStatus().getClose()) {
  23. throw new VirtualAccountException(
  24. VirtualAccountException.ACCOUNT_CLOSE, "帐户已关闭");
  25. }
  26. virtualAccount.moneyIn(command.getAmount());
  27. return virtualAccount.getBalance();
  28. }
  29. }
3.3.3 冻结
  1. package com.lehumall.va.command;
  2. import lazypack.common.component.BaseCommand;
  3. /**
  4. * 冻结金额
  5. *
  6. * @author yuxiaoxiang
  7. *
  8. */
  9. @SuppressWarnings("serial")
  10. public class MoneyFrozenCommand extends BaseCommand {
  11. /**
  12. * 帐户id
  13. */
  14. private String accountId;
  15. /**
  16. * 冻结的金额
  17. */
  18. private Double amount;
  19. }
  1. @Service
  2. @Transactional
  3. public class VirtualAccountServiceImpl extends
  4. BaseServiceImpl<VirtualAccount, VirtualAccountQO, VirtualAccountDao> {
  5. @Autowired
  6. private VirtualAccountDao virtualAccountDao;
  7. /**
  8. * 冻结金额
  9. *
  10. * @param command
  11. * @return
  12. * @throws VirtualAccountException
  13. */
  14. public Object[] moneyFrozen(MoneyFrozenCommand command)
  15. throws VirtualAccountException {
  16. VirtualAccount virtualAccount = virtualAccountDao.get(command
  17. .getAccountId());
  18. virtualAccount.moneyFrozen(command.getAmount());
  19. // 创建存冻结余额的冻结帐户
  20. CreateVirtualAccountCommand createCommand = new CreateVirtualAccountCommand();
  21. createCommand.setHyCurrencyId(virtualAccount.getCurrency().getId());
  22. createCommand.setParentId(virtualAccount.getId());
  23. createCommand.setOnwerId(virtualAccount.getOnwer().getOutId());
  24. createCommand.setOnwerType(virtualAccount.getOnwer().getType());
  25. createCommand.setName(virtualAccount.getBaseInfo().getName() + "冻结帐户");
  26. createCommand.setOnwerName(virtualAccount.getOnwer().getOutName());
  27. VirtualAccount frozenVirtualAccount = createVirtualAccount(createCommand);
  28. frozenVirtualAccount.moneyIn(command.getAmount());
  29. frozenVirtualAccount.moneyFrozen(command.getAmount());
  30. getDao().save(frozenVirtualAccount);
  31. getDao().update(virtualAccount);
  32. Object[] results = new Object[2];
  33. results[0] = virtualAccount.getBalance();
  34. results[1] = frozenVirtualAccount.getId();
  35. return results;
  36. }
3.3.4 转帐
  1. package com.lehumall.va.command;
  2. import lazypack.common.component.BaseCommand;
  3. /**
  4. * 转帐
  5. *
  6. * @author yuxiaoxiang
  7. *
  8. */
  9. @SuppressWarnings("serial")
  10. public class MoneyTransferCommand extends BaseCommand {
  11. /**
  12. * 入帐帐户id
  13. */
  14. private String inAccountId;
  15. /**
  16. * 出帐帐户id
  17. */
  18. private String outAccountId;
  19. /**
  20. * 冻结id 有值时出帐方使用冻结的金额做扣除,金额要和冻结的对应金额相等 为空时使用出帐方可用余额扣除
  21. */
  22. private String frozenId;
  23. /**
  24. * 转帐金额
  25. */
  26. private Double amount;
  27. }
  1. @Service
  2. @Transactional
  3. public class VirtualAccountServiceImpl extends
  4. BaseServiceImpl<VirtualAccount, VirtualAccountQO, VirtualAccountDao> {
  5. @Autowired
  6. private VirtualAccountDao virtualAccountDao;
  7. /**
  8. * 转帐
  9. *
  10. * @param command
  11. * @return
  12. * @throws VirtualAccountException
  13. */
  14. @Transactional(rollbackFor = VirtualAccountException.class)
  15. public VirtualAccountBalance[] moneyTransfer(MoneyTransferCommand command)
  16. throws VirtualAccountException {
  17. VirtualAccount inAccount = virtualAccountDao.get(command
  18. .getInAccountId());
  19. VirtualAccount outAccount = virtualAccountDao.get(command
  20. .getOutAccountId());
  21. inAccount.moneyIn(command.getAmount());
  22. if (command.getFrozenId() == null) {
  23. // 从可用余额付款
  24. outAccount.moneyOutFromAvaiable(command.getAmount());
  25. } else {
  26. // 从冻结金额付款
  27. VirtualAccount frozenVirtualAccount = virtualAccountDao.get(command
  28. .getFrozenId());
  29. outAccount.moneyOutFromFrozen(command.getAmount(),
  30. frozenVirtualAccount);
  31. }
  32. VirtualAccountBalance[] virtualAccountBalances = new VirtualAccountBalance[2];
  33. virtualAccountBalances[0] = inAccount.getBalance();
  34. virtualAccountBalances[1] = outAccount.getBalance();
  35. return virtualAccountBalances;
  36. }

3.4 service和dao

在具体的业务操作Service实现类中,只有接收Command对象的写操作,接收QO对象的查询操作queryUnique(QO qo)queryList(QO qo)queryPagination(QO qo)queryCount(QO qo)全部都在BaseServiceImpl中。

每个具体实体泛型对应的Dao实现类,也只有一个重写BaseDaoCriteria buildCriteria(Criteria criteria, XXXQO qo)方法,该方法中组装的Criteria条件对象可复用于上述四种查询方法。

3.4.1 UserServiceImpl
  1. package com.lehumall.application.service.user;
  2. import lazypack.common.component.BaseServiceImpl;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Transactional;
  6. import com.lehumall.application.dao.user.UserDao;
  7. import com.lehumall.domain.entity.user.user.User;
  8. import com.lehumall.pojo.qo.UserQO;
  9. @Service
  10. @Transactional
  11. public class UserServiceImpl extends BaseServiceImpl<User, UserQO, UserDao> {
  12. @Autowired
  13. private UserDao userDao;
  14. @Override
  15. protected UserDao getDao() {
  16. return userDao;
  17. }
  18. }
3.4.2 UserGradeServiceImpl
  1. package com.lehumall.application.service.user;
  2. import lazypack.common.component.BaseServiceImpl;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Transactional;
  6. import com.lehumall.application.dao.user.UserGradeDao;
  7. import com.lehumall.domain.entity.user.usergrade.UserGrade;
  8. import com.lehumall.pojo.qo.UserGradeQO;
  9. @Service
  10. @Transactional
  11. public class UserGradeServiceImpl extends BaseServiceImpl<UserGrade, UserGradeQO, UserGradeDao> {
  12. @Autowired
  13. private UserGradeDao userGradeDao;
  14. @Override
  15. protected UserGradeDao getDao() {
  16. return userGradeDao;
  17. }
  18. }
3.4.3 UserDao
  1. package com.lehumall.application.dao.user;
  2. import lazypack.common.component.BaseDao;
  3. import org.hibernate.Criteria;
  4. import org.hibernate.sql.JoinType;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Repository;
  7. import com.lehumall.domain.entity.user.user.User;
  8. import com.lehumall.pojo.qo.UserQO;
  9. @Repository
  10. public class UserDao extends BaseDao<User, UserQO> {
  11. @Autowired
  12. private UserGradeDao gradeDao;
  13. @Override
  14. protected Criteria buildCriteria(Criteria criteria, UserQO qo) {
  15. if (qo != null) {
  16. // 这里可以将在UserQO的注解中没有完善的更多查询条件加入criteria,实现更复杂的查询
  17. // ...
  18. // 将关联表的查询条件纳入条件解析
  19. if(qo.getUserGradeQO() != null){
  20. Criteria gradeCriteria = criteria.createCriteria("grade", "g",
  21. JoinType.LEFT_OUTER_JOIN);
  22. gradeDao.buildCriteriaOut(gradeCriteria, qo.getUserGradeQO());
  23. }
  24. return criteria;
  25. }
  26. return criteria;
  27. }
  28. @Override
  29. protected Class<User> getEntityClass() {
  30. return User.class;
  31. }
  32. }
3.4.4 UserGradeDao
  1. package com.lehumall.application.dao.user;
  2. import lazypack.common.component.BaseDao;
  3. import org.hibernate.Criteria;
  4. import org.springframework.stereotype.Repository;
  5. import com.lehumall.domain.entity.user.usergrade.UserGrade;
  6. import com.lehumall.pojo.qo.UserGradeQO;
  7. @Repository
  8. public class UserGradeDao extends BaseDao<UserGrade, UserGradeQO> {
  9. @Override
  10. protected Criteria buildCriteria(Criteria criteria,UserGradeQO qo) {
  11. if (qo != null) {
  12. // 这里可以将在UserGradeQO的注解中没有完善的更多查询条件加入criteria,实现更复杂的查询
  13. // ...
  14. return criteria;
  15. }
  16. return criteria;
  17. }
  18. @Override
  19. protected Class<UserGrade> getEntityClass() {
  20. return UserGrade.class;
  21. }
  22. }

4. 数据查询测试代码演示

以下代码可以直观地看出如何去使用封装好的工具,这些代码通常是写在ActionController中的代码,不需要往ServiceDao中添加任何方法就能运行,依赖lazypack-common的基本封装类和方法。

全部代码已经通过测试,JUNIT测试类位于hy-lehu-application工程src/test/java目录下。

4.1 单条件单条结果查询

按id查询:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. // 按帐号id查询
  3. qo.setId("001");
  4. VirtualAccount va = virtualAccountService.queryUnique(qo);

效果:

  1. select
  2. this_.ID as ID1_8_0_,
  3. this_.AVAIABLE_AMOUNT as AVAIABLE2_8_0_,
  4. this_.FROZEN_AMOUNT as FROZEN_A3_8_0_,
  5. this_.TOTAL_AMOUNT as TOTAL_AM4_8_0_,
  6. this_.CREATE_DATE as CREATE_D5_8_0_,
  7. this_.INVALID_DATE as INVALID_6_8_0_,
  8. this_.NAME as NAME7_8_0_,
  9. this_.PROXY_ACCOUNT as PROXY_AC8_8_0_,
  10. this_.CURRENCY_ID as CURRENC14_8_0_,
  11. this_.OUT_ID as OUT_ID9_8_0_,
  12. this_.OUT_NAME as OUT_NAM10_8_0_,
  13. this_.DOMAIN_TYPE as DOMAIN_11_8_0_,
  14. this_.PARENT_ID as PARENT_15_8_0_,
  15. this_.CLOSE as CLOSE12_8_0_,
  16. this_.ENABLE as ENABLE13_8_0_
  17. from
  18. VA_VIRTUAL_ACCOUNT this_
  19. where
  20. this_.ID='001' limit 1

4.2 字段投影筛选

加上字段投影筛选:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. // 按帐号id查询
  3. qo.setId("001");
  4. // 只查两个字段
  5. String[] projs = {"id","baseInfo.name"};
  6. qo.setProjectionProperties(projs);
  7. VirtualAccount va = virtualAccountService.queryUnique(qo);

注:第二个字段写成baseInfo.userName是因为name这个属性放在VirtualAccount中的名为baseInfo的值对象中,见3.1.1

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.ID='001' limit 1

4.3 多条结果查询

多条查询:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. // 查状态为已启用的帐户
  3. qo.setEnable(true);
  4. // 只查两个字段
  5. String[] projs = {"id","baseInfo.name"};
  6. qo.setProjectionProperties(projs);
  7. List<VirtualAccount> vaList = virtualAccountService.queryList(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.ENABLE='Y'

3.4 分页查询

分页查询:

  1. Pagination pagination = new Pagination();
  2. // 查第1页每页5条
  3. pagination.setPageNo(1);
  4. pagination.setPageSize(5);
  5. VirtualAccountQO qo = new VirtualAccountQO();
  6. // 查状态为已启用的帐户
  7. qo.setEnable(true);
  8. // 只查两个字段
  9. String[] projs = {"id","baseInfo.name"};
  10. qo.setProjectionProperties(projs);
  11. pagination.setCondition(qo);
  12. pagination = virtualAccountService.queryPagination(pagination);
  13. System.out.println(pagination.getList());

效果:

  1. select
  2. count(*) as y0_
  3. from
  4. VA_VIRTUAL_ACCOUNT this_
  5. where
  6. this_.ENABLE='Y'
  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.ENABLE='Y' limit 1

3.5 带范围条件查询

范围查询:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. // 查帐户可用余额大于等于0并且小于等于10的帐户
  3. qo.setGeAvaiableAmount(0D);
  4. qo.setLeAvaiableAmount(10D);
  5. String[] projs = {"id","baseInfo.name"};
  6. qo.setProjectionProperties(projs);
  7. virtualAccountRemoteService.queryList(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.AVAIABLE_AMOUNT>=0
  8. and this_.AVAIABLE_AMOUNT<=10

3.6 对延迟加载的关联实体做立即查询

立即加载被配置成lazy的关联实体

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. qo.setId("001");
  3. qo.setFetchCurrency(true);
  4. String[] projs = {"id","baseInfo.name"};
  5. qo.setProjectionProperties(projs);
  6. virtualAccountService.queryUnique(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.ID='001' limit 1

上面没有出现join效果,这是因为我们筛选了列,并且没有获取HyCurrency实体里的列,所以Hibernate给优化掉了。

修改上面的条件:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. qo.setId("001");
  3. qo.setFetchCurrency(true);
  4. // String[] projs = {"id","baseInfo.name"};
  5. // qo.setProjectionProperties(projs);
  6. virtualAccountService.queryUnique(qo);

效果:

  1. select
  2. this_.ID as ID1_8_1_,
  3. this_.AVAIABLE_AMOUNT as AVAIABLE2_8_1_,
  4. this_.FROZEN_AMOUNT as FROZEN_A3_8_1_,
  5. this_.TOTAL_AMOUNT as TOTAL_AM4_8_1_,
  6. this_.CREATE_DATE as CREATE_D5_8_1_,
  7. this_.INVALID_DATE as INVALID_6_8_1_,
  8. this_.NAME as NAME7_8_1_,
  9. this_.PROXY_ACCOUNT as PROXY_AC8_8_1_,
  10. this_.CURRENCY_ID as CURRENC14_8_1_,
  11. this_.OUT_ID as OUT_ID9_8_1_,
  12. this_.OUT_NAME as OUT_NAM10_8_1_,
  13. this_.DOMAIN_TYPE as DOMAIN_11_8_1_,
  14. this_.PARENT_ID as PARENT_15_8_1_,
  15. this_.CLOSE as CLOSE12_8_1_,
  16. this_.ENABLE as ENABLE13_8_1_,
  17. hycurrency2_.ID as ID1_7_0_,
  18. hycurrency2_.ENABLE as ENABLE2_7_0_,
  19. hycurrency2_.EXCHANGE_ONE_YUAN as EXCHANGE3_7_0_,
  20. hycurrency2_.NAME as NAME4_7_0_,
  21. hycurrency2_.PROXY_CURRENCY as PROXY_CU5_7_0_,
  22. hycurrency2_.UNIT as UNIT6_7_0_
  23. from
  24. VA_VIRTUAL_ACCOUNT this_
  25. inner join
  26. VA_HY_CURRENCY hycurrency2_
  27. on this_.CURRENCY_ID=hycurrency2_.ID
  28. where
  29. this_.ID='001' limit 1

3.7 对关联实体中的列进行筛选查询

两张表的所有字段都出来了,然后我们会需要筛选。要动用到关联实体的QO。

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. qo.setId("001");
  3. // 立即加载帐户所用货币信息
  4. qo.setFetchCurrency(true);
  5. // 从帐户表查两个字段
  6. String[] projs = {"id","baseInfo.name"};
  7. qo.setProjectionProperties(projs);
  8. // 从货币表查两个字段
  9. HyCurrencyQO qo2 = new HyCurrencyQO();
  10. String[] projs2 = {"id","name"};
  11. qo2.setProjectionProperties(projs2);
  12. qo.setHyCurrencyQO(qo2);
  13. virtualAccountService.queryUnique(qo);

因为操作了关联实体的查询条件,所以我们还要在Dao里做些额外工作让上面的qo.setUserGradeQO(userGradeQO);生效。这里只有片段,完整的UserDao4.3.3

  1. @Override
  2. protected Criteria buildCriteria(Criteria criteria, VirtualAccountQO qo) {
  3. if (qo != null) {
  4. if (qo.getHyCurrencyQO() != null) {
  5. // 创建一个子查询Criteria用于组装HyCurrency实体上的查询条件
  6. Criteria hyCurrencyCriteria = criteria.createCriteria("currency", "hc", JoinType.LEFT_OUTER_JOIN);
  7. // 将yCurrencyQO里的查询条件加载到子查询Criteria里
  8. hyCurrencyDao.buildCriteriaOut(hyCurrencyCriteria, qo.getHyCurrencyQO());
  9. }
  10. }
  11. return criteria;
  12. }

开发人员要写的所有查询代码已经全部在上面了,没有其他要写的了。
效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_,
  4. hc1_.ID as y2_,
  5. hc1_.NAME as y3_
  6. from
  7. VA_VIRTUAL_ACCOUNT this_
  8. left outer join
  9. VA_HY_CURRENCY hc1_
  10. on this_.CURRENCY_ID=hc1_.ID
  11. where
  12. this_.ID='001' limit 1

3.8 以关联实体中的列做为条件查询

同时按User和UserGrade里的值做条件查询:

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. qo.setId("001");
  3. qo.setFetchCurrency(true);
  4. String[] projs = {"id","baseInfo.name"};
  5. qo.setProjectionProperties(projs);
  6. HyCurrencyQO qo2 = new HyCurrencyQO();
  7. String[] projs2 = {"id","name"};
  8. qo2.setProjectionProperties(projs2);
  9. qo2.setName("积分");
  10. qo.setHyCurrencyQO(qo2);
  11. virtualAccountService.queryUnique(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_,
  4. hc1_.ID as y2_,
  5. hc1_.NAME as y3_
  6. from
  7. VA_VIRTUAL_ACCOUNT this_
  8. left outer join
  9. VA_HY_CURRENCY hc1_
  10. on this_.CURRENCY_ID=hc1_.ID
  11. where
  12. this_.ID='001'
  13. and hc1_.NAME='积分' limit 1

3.9 模糊查询

  1. VirtualAccountQO qo = new VirtualAccountQO();
  2. qo.setId("001");
  3. qo.setFetchCurrency(true);
  4. String[] projs = {"id","baseInfo.name"};
  5. qo.setProjectionProperties(projs);
  6. qo.setName("分");
  7. qo.setNameLike(true);
  8. virtualAccountService.queryUnique(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.NAME as y1_
  4. from
  5. VA_VIRTUAL_ACCOUNT this_
  6. where
  7. this_.NAME like '%分%'
  8. and this_.ID='001'limit 1

4.5 总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注