[关闭]
@iamfox 2015-07-02T16:20:43.000000Z 字数 23589 阅读 2342

对乐虎网业务逻辑层和持久层重构的效果(待续)

汇银


1. 背景和改造思路

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

以下是基本改造思路:

数据库不动,另建java工程对原有库表进行操作,针对现有库表建立新的service层,dao层和entity业务实体。
目前新建了两个maven工程如下:

工程 主要内容 依赖
hy-lehu-application 包含service、dao层、entity类 hy-lehu-pojo、lazypack-common
hy-lehu-pojo 包含跨层传递的参数对象,各种javabean lazypack-common

这两个工程也可以用来开发乐虎网上后续可以独立开发的新增功能,接下来将用它来实现对wap端进行2000个入口的微信推广功能中的业务逻辑和实体、库表开发。

将来模块化设计后还会对这两个工程做细化拆分。

所有代码已提交至新的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. 数据查询测试代码演示

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

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

3.1 单条件单条结果查询

按id查询:

  1. UserQO qo = new UserQO();
  2. // 按用户id查询
  3. qo.setId(2550L);
  4. User user = userServiceImpl.queryUnique(qo);

效果:

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as ID1_2_1_,
  6. this_.LOGIN_NAME as LOGIN_NA2_2_1_,
  7. this_.PASSWORD as PASSWORD3_2_1_,
  8. this_.USER_NAME as USER_NAM4_2_1_,
  9. ...字段过多,这里是全部字段查询,此处省略
  10. from
  11. T_USER this_
  12. where
  13. this_.ID=2550L )
  14. where
  15. rownum <= 1

3.2 字段投影筛选

加上字段投影筛选:

  1. UserQO qo = new UserQO();
  2. qo.setId(2550L);
  3. // 只查两个字段
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. User user = userServiceImpl.queryUnique(qo);

注:第二个字段写成authInfo.userName是因为userName这个属性放在User中的名为authInfo的值对象中,见4.1.1

效果:

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as y0_,
  6. this_.USER_NAME as y1_
  7. from
  8. T_USER this_
  9. where
  10. this_.ID=2550 )
  11. where
  12. rownum <= 1

3.3 多条结果查询

多条查询:

  1. UserQO qo = new UserQO();
  2. // 查所在城市标识为999的用户
  3. qo.setCity(999);
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. List<User> userList = userServiceImpl.queryList(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.USER_NAME as y1_
  4. from
  5. T_USER this_
  6. where
  7. this_.CITY=999

3.4 分页查询

分页查询:

  1. UserQO qo = new UserQO();
  2. // 查第二页,每页五条
  3. Pagination pagination = new Pagination();
  4. pagination.setPageNo(2);
  5. pagination.setPageSize(5);
  6. pagination.setCondition(qo);
  7. qo.setCity(999);
  8. String[] projs = {"id","authInfo.userName"};
  9. qo.setProjectionProperties(projs);
  10. pagination = userServiceImpl.queryPagination(pagination);
  11. List<User> list = (List<User>) pagination.getList();

效果:

  1. select
  2. count(*) as y0_
  3. from
  4. T_USER this_
  5. where
  6. (
  7. this_.CITY=?
  8. )
  1. select
  2. *
  3. from
  4. ( select
  5. row_.*,
  6. rownum rownum_
  7. from
  8. ( select
  9. this_.ID as y0_,
  10. this_.USER_NAME as y1_
  11. from
  12. T_USER this_
  13. where
  14. (
  15. this_.CITY=999
  16. ) ) row_
  17. where
  18. rownum <= 10
  19. )
  20. where
  21. rownum_ > 5

3.5 带范围条件查询

范围查询:

  1. Calendar c1 = Calendar.getInstance();
  2. c1.set(Calendar.YEAR, 2015);
  3. c1.set(Calendar.MONTH, 4);
  4. c1.set(Calendar.DAY_OF_MONTH, 1);
  5. UserQO qo = new UserQO();
  6. // 查注册日期在5月1日至今的用户
  7. qo.setGtRegTimestamp(c1.getTime());
  8. qo.setLtRegTimestamp(new Date());
  9. String[] projs = {"id","authInfo.userName"};
  10. qo.setProjectionProperties(projs);
  11. userServiceImpl.queryList(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.USER_NAME as y1_
  4. from
  5. T_USER this_
  6. where
  7. this_.REGISTRATIONTIMETAMP<=?
  8. and this_.REGISTRATIONTIMETAMP>=?

注:很遗憾目前的乐虎数据库不支持这条日期范围查询SQL,因为数据库里日期并没有被设为DATE型,而受制于该版本的jdbc驱动,如果将java.util.Date映射到其Timestamp类型上会产生异常,只能暂时使用String来映射Timestamp。对数据库中使用非DATE类型的日期,需要另行编写SQL。

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

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

  1. UserQO qo = new UserQO();
  2. qo.setId(2550L);
  3. qo.setFetchUserGrade(true);
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. User user = userServiceImpl.queryUnique(qo);

效果:

  1. select
  2. this_.ID as y0_,
  3. this_.USER_NAME as y1_
  4. from
  5. T_USER this_
  6. where
  7. this_.ID=2550

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

修改上面的条件:

  1. UserQO qo = new UserQO();
  2. qo.setId(2550L);
  3. qo.setFetchUserGrade(true);
  4. // String[] projs = {"id","authInfo.userName"};
  5. // qo.setProjectionProperties(projs);
  6. User user = userServiceImpl.queryUnique(qo);

效果:

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as ID1_2_1_,
  6. this_.LOGIN_NAME as LOGIN_NA2_2_1_,
  7. this_.PASSWORD as PASSWORD3_2_1_,
  8. ...省略
  9. this_.SIGNDAY_TIME as SIGNDAY54_2_1_,
  10. this_.SIGNDAY_TIME_TAMP as SIGNDAY55_2_1_,
  11. usergrade2_.ID as ID1_3_0_,
  12. usergrade2_.CREATETIME as CREATETI2_3_0_,
  13. usergrade2_.CREARTTIMETAMP as CREARTTI3_3_0_,
  14. ...省略
  15. usergrade2_.MIN_GROWTH_VALUE as MIN_GRO16_3_0_,
  16. usergrade2_.SORT as SORT17_3_0_
  17. from
  18. T_USER this_
  19. left outer join
  20. T_USER_GRADE usergrade2_
  21. on this_.USER_GRADE_ID=usergrade2_.ID
  22. where
  23. this_.ID=2550 )
  24. where
  25. rownum <= 1

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

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

  1. UserQO qo = new UserQO();
  2. qo.setId(2550L);
  3. // 从User取两个字段
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. // 从UserGrade取两个字段
  7. UserGradeQO userGradeQO = new UserGradeQO();
  8. String[] projs2 = {"id", "baseInfo.title"};
  9. userGradeQO.setProjectionProperties(projs2);
  10. // 将两个查询条件对象合二为一
  11. qo.setUserGradeQO(userGradeQO);
  12. User user = userServiceImpl.queryUnique(qo);

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

  1. @Override
  2. protected Criteria buildCriteria(Criteria criteria, UserQO qo) {
  3. if (qo != null) {
  4. // 将关联表的查询条件纳入条件解析
  5. if(qo.getUserGradeQO() != null){
  6. // 创建一个子查询Criteria用于组装UserGrade实体上的查询条件
  7. Criteria gradeCriteria = criteria.createCriteria("grade", "g",
  8. JoinType.LEFT_OUTER_JOIN);
  9. // 将UserGradeQO里的查询条件加载到子查询Criteria里
  10. gradeDao.buildCriteriaOut(gradeCriteria, qo.getUserGradeQO());
  11. }
  12. return criteria;
  13. }
  14. return criteria;
  15. }

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

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as y0_,
  6. this_.USER_NAME as y1_,
  7. g1_.ID as y2_,
  8. g1_.TITLE as y3_
  9. from
  10. T_USER this_
  11. left outer join
  12. T_USER_GRADE g1_
  13. on this_.USER_GRADE_ID=g1_.ID
  14. where
  15. this_.ID=2550 )
  16. where
  17. rownum <= 1

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

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

  1. UserQO qo = new UserQO();
  2. // 按邮箱查
  3. qo.setEmail("745040361@qq.com");
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. UserGradeQO userGradeQO = new UserGradeQO();
  7. String[] projs2 = {"id", "baseInfo.title"};
  8. userGradeQO.setProjectionProperties(projs2);
  9. // 按等级名称查
  10. userGradeQO.setTitle("普通会员");
  11. qo.setUserGradeQO(userGradeQO);
  12. User user = userServiceImpl.queryUnique(qo);

效果:

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as y0_,
  6. this_.USER_NAME as y1_,
  7. g1_.ID as y2_,
  8. g1_.TITLE as y3_
  9. from
  10. T_USER this_
  11. left outer join
  12. T_USER_GRADE g1_
  13. on this_.USER_GRADE_ID=g1_.ID
  14. where
  15. (
  16. this_.EMAIL='abc@163.com'
  17. )
  18. and g1_.TITLE='普通会员' )
  19. where
  20. rownum <= 1

3.9 模糊查询

  1. UserQO qo = new UserQO();
  2. qo.setEmail("745040361@qq.com");
  3. qo.setEmailLike(true);
  4. String[] projs = {"id","authInfo.userName"};
  5. qo.setProjectionProperties(projs);
  6. User user = userServiceImpl.queryUnique(qo);

效果:

  1. select
  2. *
  3. from
  4. ( select
  5. this_.ID as y0_,
  6. this_.USER_NAME as y1_
  7. from
  8. T_USER this_
  9. where
  10. (
  11. this_.EMAIL like ?
  12. ) )
  13. where
  14. rownum <= 1

4. 必要的类介绍

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

4.1 业务实体

以用户表(T_USER),用户会员级别表(T_USER_GRADE),汇银地点表(T_SIDE_HUIYIN)、行业信息表(T_INDUSTRY_HOBBY)为例。

四张表的关联关系是
T_USERT_USER_GRADE多对一, 每个会员级别有多个用户,每个用户只属于一个会员级别,外键是USER_GRADE_ID
T_USERT_SIDE_HUIYIN多对一,每个用户有一个注册/登录的附近门店,外键是NEARBYID,即T_SIDE_HUIYIN的主键。
T_USERT_INDUSTRY_HOBBY多对一。

以该四张表建立四个对应的主要根实体UserUserGradeSideHuiyinIndustry

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

注:由于数据库表历史原因及oracle版本和对应的jdbc驱动包所限,某些字段映射类型不合理,暂迁就历史数据库,确保能正常运行。以下代码均已经过测试。

4.1.1 User
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Entity;
  3. import javax.persistence.FetchType;
  4. import javax.persistence.JoinColumn;
  5. import javax.persistence.ManyToOne;
  6. import javax.persistence.Table;
  7. import com.lehumall.domain.base.Industry;
  8. import com.lehumall.domain.user.sidehuiyin.SideHuiyin;
  9. import com.lehumall.domain.user.usergrade.UserGrade;
  10. import lazypack.common.component.LongIdBaseEntity;
  11. @Entity
  12. @Table(name = "T_USER")
  13. @SuppressWarnings("serial")
  14. public class User extends LongIdBaseEntity {
  15. /**
  16. * 用户基本信息
  17. */
  18. private UserBaseInfo baseInfo;
  19. /**
  20. * 用户认证信息
  21. */
  22. private UserAuthInfo authInfo;
  23. /**
  24. * 用户绑定的外部标识
  25. */
  26. private UserBindingIDs bindingIDs;
  27. /**
  28. * 用户联系信息
  29. */
  30. private UserContactInfo contactInfo;
  31. /**
  32. * 用户状态信息
  33. */
  34. private UserStatus status;
  35. /**
  36. * 用户所属行业
  37. */
  38. @ManyToOne(fetch = FetchType.LAZY)
  39. @JoinColumn(name = "INDUSTRY_ID")
  40. private Industry industry;
  41. /**
  42. * 用户会员等级
  43. */
  44. @ManyToOne(fetch = FetchType.LAZY)
  45. @JoinColumn(name = "USER_GRADE_ID")
  46. private UserGrade grade;
  47. /**
  48. * 注册/登录的附近门店
  49. */
  50. @ManyToOne(fetch = FetchType.LAZY)
  51. @JoinColumn(name = "NEARBYID")
  52. private SideHuiyin nearBy;
  53. // getter/setter省略
  54. }
4.1.2 UserAuthInfo
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. /**
  5. * 用户认证信息
  6. * @author yuxiaoxiang
  7. *
  8. */
  9. @Embeddable
  10. public class UserAuthInfo {
  11. /**
  12. * 用户名
  13. */
  14. @Column(name = "USER_NAME")
  15. private String userName;
  16. /**
  17. * 登录名(彩票用户)
  18. */
  19. @Column(name = "LOGIN_NAME")
  20. private String loginName;
  21. /**
  22. * 密码
  23. */
  24. @Column(name = "PASSWORD")
  25. private String password;
  26. }
4.1.3 UserBaseInfo
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. /**
  5. * 用户基本信息
  6. *
  7. * @author yuxiaoxiang
  8. *
  9. */
  10. @Embeddable
  11. public class UserBaseInfo {
  12. /**
  13. * 真实姓名
  14. */
  15. @Column(name = "REAL_NAME")
  16. private String realName;
  17. /**
  18. * 性别
  19. */
  20. @Column(name = "SEX")
  21. private Integer sex;
  22. public final static Integer SEX_MALE = 1; // 男
  23. public final static Integer SEX_FEMAIL = 0; // 女
  24. /**
  25. * 生日
  26. */
  27. @Column(name = "BIRTHDAY")
  28. private String birthday;
  29. /**
  30. * 身份证号
  31. */
  32. @Column(name = "CARD_NUMBER")
  33. private String cardNumber;
  34. /**
  35. * 婚姻状态
  36. */
  37. @Column(name = "MARITAL_STATUS")
  38. private Integer maritalStatus;
  39. public final static Integer MARITAL_STATUS_NO = 1; // 未婚
  40. public final static Integer MARITAL_STATUS_YES = 2; // 已婚
  41. public final static Integer MARITAL_STATUS_SECRET = 3; // 保密
  42. /**
  43. * 月收入 月收入(1,2000元以下,2.2000-3999元 3.4000-5999元,4,6000-7999元,5,8000元以上)
  44. */
  45. @Column(name = "MONTHLY_INCOME")
  46. private Integer monthlyIncome;
  47. public final static Integer MONTHLY_INCOME_2000 = 1;
  48. public final static Integer MONTHLY_INCOME_2000_3999 = 2;
  49. public final static Integer MONTHLY_INCOME_4000_5999 = 3;
  50. public final static Integer MONTHLY_INCOME_6000_7999 = 4;
  51. public final static Integer MONTHLY_INCOME_8000 = 5;
  52. /**
  53. * 教育程度( 1初中, 2高中,3中专,4大专,5本科,6硕士,7博士,8其他)
  54. */
  55. @Column(name = "EDUCATION")
  56. private Integer education;
  57. public final static Integer EDUCATION_CZ = 1;
  58. public final static Integer EDUCATION_GZ = 2;
  59. public final static Integer EDUCATION_ZZ = 3;
  60. public final static Integer EDUCATION_DZ = 4;
  61. public final static Integer EDUCATION_BK = 5;
  62. public final static Integer EDUCATION_SS = 6;
  63. public final static Integer EDUCATION_BS = 7;
  64. public final static Integer EDUCATION_QT = 8;
  65. /**
  66. * 用户头像
  67. */
  68. @Column(name = "USER_URL")
  69. private String userUrl;
  70. /**
  71. * 排序
  72. */
  73. @Column(name = "SORT")
  74. private Integer sort;
  75. /**
  76. * 来源: 1: web 2: pc 3:android, 4:ios 5:wp 6.微信
  77. */
  78. @Column(name = "ORIGIN")
  79. private Integer origin;
  80. public final static Integer ORIGIN_WEB = 1;
  81. public final static Integer ORIGIN_PC = 2;
  82. public final static Integer ORIGIN_ANDROID = 3;
  83. public final static Integer ORIGIN_IOS = 4;
  84. public final static Integer ORIGIN_WP = 5;
  85. public final static Integer ORIGIN_WX = 6;
  86. /**
  87. * 用户类型(1,个人用户。2,企业用户)
  88. */
  89. @Column(name = "USER_TYPE")
  90. private Integer type;
  91. public final static Integer TYPE_PERSON = 1; // 个人用户
  92. public final static Integer TYPE_COMPANY = 2; // 企业用户
  93. /**
  94. * 是否是老用户1,不是老用户 2是老用户
  95. */
  96. @Column(name = "OLD_TYPE")
  97. private Integer oldType;
  98. public final static Integer OLD_TYPE_NO = 1; // 不是老用户
  99. public final static Integer OLD_TYPE_YES = 2; // 是老用户
  100. /**
  101. * 注册时间
  102. */
  103. @Column(name = "REGISTRATION_TIME")
  104. private String registrationTime;
  105. /**
  106. * 注册时间戳
  107. */
  108. @Column(name = "REGISTRATIONTIMETAMP")
  109. private String registrationTimestamp;
  110. }
4.1.4 UserBindingIds
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. /**
  5. * 用户绑定的标识
  6. *
  7. * @author yuxiaoxiang
  8. *
  9. */
  10. @Embeddable
  11. public class UserBindingIds {
  12. /**
  13. * 手机设备号
  14. */
  15. @Column(name = "PHONE_TOKEN")
  16. private String phoneToken;
  17. /**
  18. * QQ登录的OPEN ID
  19. */
  20. @Column(name = "QQ_OPEN_ID")
  21. private String qqOpenId;
  22. /**
  23. * 微博登录的OPEN ID
  24. */
  25. @Column(name = "WEIBO_OPEN_ID")
  26. private String weiboOpenId;
  27. /**
  28. * 支付宝登录的OPEN ID
  29. */
  30. @Column(name = "ALIPAY_OPEN_ID")
  31. private String alipayOpenId;
  32. /**
  33. * 百度登录的OPEN ID
  34. */
  35. @Column(name = "BAIDU_OPEN_ID")
  36. private String baiduOpenId;
  37. /**
  38. * 微信登录的OPEN ID
  39. */
  40. @Column(name = "WEIXIN")
  41. private String weixinOpenId;
  42. }
4.1.5 UserContactInfo
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. @Embeddable
  5. public class UserContactInfo {
  6. /**
  7. * 手机
  8. */
  9. @Column(name = "PHONE")
  10. private String phone;
  11. /**
  12. * 邮箱
  13. */
  14. @Column(name = "EMAIL")
  15. private String email;
  16. /**
  17. * QQ
  18. */
  19. @Column(name = "QQ")
  20. private String qq;
  21. /**
  22. * 省
  23. */
  24. @Column(name = "PROVINCE")
  25. private Integer province;
  26. /**
  27. * 市
  28. */
  29. @Column(name = "CITY")
  30. private Integer city;
  31. /**
  32. * 区
  33. */
  34. @Column(name = "AREA")
  35. private Integer area;
  36. /**
  37. * 小区
  38. */
  39. @Column(name = "DISTRICT")
  40. private String district;
  41. /**
  42. * 详细地址
  43. */
  44. @Column(name = "DETAILS")
  45. private String details;
  46. }
4.1.6 UserStatus
  1. package com.lehumall.domain.user.user;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. /**
  5. * 用户状态
  6. * @author yuxiaoxiang
  7. *
  8. */
  9. @Embeddable
  10. public class UserStatus {
  11. /**
  12. * 手机验证状态
  13. */
  14. @Column(name = "PHONE_STATUS")
  15. private Integer phoneStatus;
  16. public final static Integer PHONE_STATUS_VALID = 1; // 已验证
  17. public final static Integer PHONE_STATUS_UNVALID = 0; // 未验证
  18. /**
  19. * 邮箱验证状态
  20. */
  21. @Column(name = "EMAIL_STATUS")
  22. private Integer emailStatus;
  23. public final static Integer EMAIL_STATUS_VALID = 1; // 已验证
  24. public final static Integer EMAIL_STATUS_UNVALID = 0; // 未验证
  25. /**
  26. * 成长值
  27. */
  28. @Column(name = "GROWTH_VALUE")
  29. private Integer growthValue;
  30. /**
  31. * 积分
  32. */
  33. @Column(name = "INTEGRAL")
  34. private Integer integral;
  35. /**
  36. * 帐号状态(1 是,0否)
  37. */
  38. @Column(name = "ENABLE")
  39. private Integer enable;
  40. /**
  41. * 购买状态(1 是,0否)
  42. */
  43. @Column(name = "BUY_STATUS")
  44. private Integer buyStatus;
  45. /**
  46. * 点评状态(1 是,0否)
  47. */
  48. @Column(name = "COMMENT_STATUS")
  49. private Integer commentStatus;
  50. /**
  51. * 登录状态(1 是,0否)
  52. */
  53. @Column(name = "LOGIN_STATUS")
  54. private Integer loginStatus;
  55. /**
  56. * 举报状态(1 是,0否)
  57. */
  58. @Column(name = "REPORT_STATUS")
  59. private Integer reportStatus;
  60. public final static Integer STATUS_ACTIVED = 1; // 已验证
  61. public final static Integer STATUS_FORBIDDEN = 0; // 未验证
  62. /**
  63. * 最后登录时间
  64. */
  65. @Column(name = "LOGIN_TIME")
  66. private String loginTime;
  67. /**
  68. * 最后登录时间戳
  69. */
  70. @Column(name = "LOGINTIMETAMP")
  71. private String loginTimestamp;
  72. /**
  73. * 最后登录IP
  74. */
  75. @Column(name = "LOGIN_IP")
  76. private String loginIP;
  77. /**
  78. * 累计签到天数
  79. */
  80. @Column(name = "SIGNDAY")
  81. private Integer signDay;
  82. /**
  83. * 最后一次签到时间
  84. */
  85. @Column(name = "SIGNDAY_TIME")
  86. private String signdayTime;
  87. /**
  88. * 最后一次签到时间戳
  89. */
  90. @Column(name = "SIGNDAY_TIME_TAMP")
  91. private Long signdayTimetamp;
  92. /**
  93. * 预付卡余额
  94. */
  95. @Column(name = "BALANCE")
  96. private Double balance;
  97. /**
  98. * 快捷服务id,多个id逗号隔开
  99. */
  100. @Column(name = "FASTIDS")
  101. private String fastIds;
  102. /**
  103. * 连续登录天数
  104. */
  105. @Column(name = "CONTINUE_LOGIN_COUNT")
  106. private Integer continueLoginCount;
  107. /**
  108. * 密码强度 1弱 2中 3强 BYTE
  109. */
  110. @Column(name = "PWD_SFAE")
  111. private Integer pwdSafe;
  112. public final static Integer PWD_SAFE_WEAK = 1;
  113. public final static Integer PWD_SAFE_NORMAL = 2;
  114. public final static Integer PWD_SAFE_STRONG = 3;
  115. /**
  116. * 是否获得过完善基本资料获得的积分0没有 1有
  117. */
  118. @Column(name = "MZJF")
  119. private Integer mzjf;
  120. /**
  121. * 是否获得完善更多资料获取的积分0没有 1有
  122. */
  123. @Column(name = "MMJF")
  124. private Integer mmjf;
  125. /**
  126. * 是否获得上传头像获取的积分0没有 1有
  127. */
  128. @Column(name = "SCTX")
  129. private Integer sctx;
  130. /**
  131. * 是否获得首次登录送虎券0没有 1有
  132. */
  133. @Column(name = "SCHQ")
  134. private Integer schq;
  135. public final static Integer JF_STATUS_YES = 0;
  136. public final static Integer JF_STATUS_NO = 1;
  137. }
4.1.7 UserGrade
  1. package com.lehumall.domain.entity.user.usergrade;
  2. import javax.persistence.Entity;
  3. import javax.persistence.Table;
  4. import lazypack.common.component.LongIdBaseEntity;
  5. /**
  6. * 用户会员等级
  7. *
  8. * @author yuxiaoxiang
  9. *
  10. */
  11. @Entity
  12. @Table(name = "T_USER_GRADE")
  13. @SuppressWarnings("serial")
  14. public class UserGrade extends LongIdBaseEntity {
  15. /**
  16. * 会员等级基本信息
  17. */
  18. private UserGradeBaseInfo baseInfo;
  19. /**
  20. * 会员等级成长信息
  21. */
  22. private UserGradeGrowthInfo growthInfo;
  23. /**
  24. * 会员等级优惠信息
  25. */
  26. private UserGradeFavorableInfo favorableInfo;
  27. }
4.1.8 UserGradeBaseInfo
  1. package com.lehumall.domain.entity.user.usergrade;
  2. import java.util.Date;
  3. import javax.persistence.Column;
  4. import javax.persistence.Embeddable;
  5. /**
  6. * 用户会员等级信息
  7. *
  8. * @author yuxiaoxiang
  9. *
  10. */
  11. @Embeddable
  12. public class UserGradeBaseInfo {
  13. @Column(name = "TITLE")
  14. private String title;
  15. @Column(name = "CREATETIME")
  16. private String createTime;
  17. @Column(name = "CREARTTIMETAMP")
  18. private String createTimestamp;
  19. @Column(name = "UPDATETIME")
  20. private String updateTime;
  21. @Column(name = "UPDATETIMETAMP")
  22. private String updateTimestamp;
  23. /**
  24. * 是否启用
  25. */
  26. @Column(name = "ENABLE")
  27. private Integer enable;
  28. public final static Integer ENABLE_ACTIVED = 1; // 启用
  29. public final static Integer ENABLE_FORBIDDEN = 2; // 禁用
  30. /**
  31. * 等级图片
  32. */
  33. @Column(name = "URL")
  34. private String url;
  35. /**
  36. * 会员类型
  37. */
  38. @Column(name = "TYPE")
  39. private Integer type;
  40. public final static Integer TYPE_PERSON = 0; // 个人
  41. public final static Integer TYPE_COMPANY = 1; // 企业
  42. /**
  43. * 说明
  44. */
  45. @Column(name = "EXPLAIN")
  46. private String explain;
  47. }
4.1.9 UserGradeGrowthInfo
  1. package com.lehumall.domain.entity.user.usergrade;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. @Embeddable
  5. public class UserGradeGrowthInfo {
  6. /**
  7. * 排序
  8. */
  9. @Column(name = "SORT")
  10. private Integer sort;
  11. /**
  12. * 最大成长值
  13. */
  14. @Column(name = "MAX_GROWTH_VALUE")
  15. private Integer maxGrowthValue;
  16. /**
  17. * 最小成长值
  18. */
  19. @Column(name = "MIN_GROWTH_VALUE")
  20. private Integer minGrowthValue;
  21. }
4.1.10 UserGradeFavorableInfo
  1. package com.lehumall.domain.entity.user.usergrade;
  2. import javax.persistence.Column;
  3. import javax.persistence.Embeddable;
  4. /**
  5. * 会员级别优惠信息
  6. *
  7. * @author yuxiaoxiang
  8. *
  9. */
  10. @Embeddable
  11. public class UserGradeFavorableInfo {
  12. /**
  13. * 运费状态
  14. */
  15. @Column(name = "FREE_STATUS")
  16. private Integer freeStatus;
  17. public final static Integer FREE_STATUS_ACTIVED = 1; // 启用
  18. public final static Integer FREE_STATUS_FORBIDDEN = 2; // 禁用
  19. /**
  20. * 免运费金额
  21. */
  22. @Column(name = "FREE_MONEY")
  23. private Integer freeMoney;
  24. /**
  25. * 折扣状态
  26. */
  27. @Column(name = "DISCOUNT_STATUS")
  28. private Integer discountStatus;
  29. public final static Integer DISCOUNT_STATUS_ACTIVED = 1; // 启用
  30. public final static Integer DISCOUNT_STATUS_FORBIDDEN = 2; // 禁用
  31. /**
  32. * 折扣金额
  33. */
  34. @Column(name = "DISCOUNT")
  35. private Integer discount;
  36. }

4.2 查询条件类QO

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

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

4.2.1 UserQO
  1. package com.lehumall.pojo.qo;
  2. import java.util.Date;
  3. import lazypack.common.annotation.QueryCondition;
  4. import lazypack.common.annotation.QueryConditionType;
  5. import lazypack.common.annotation.QueryConfig;
  6. import lazypack.common.component.BaseQO;
  7. @QueryConfig(daoBeanId = "userDao")
  8. @SuppressWarnings("serial")
  9. public class UserQO extends BaseQO<Long> {
  10. /**
  11. * 按用户名查询,如果userNameLike设为true模糊查询,否则精确查询
  12. */
  13. @QueryCondition(name = "authInfo.userName", ifTrueUseLike = "userNameLike")
  14. private String userName;
  15. /**
  16. * 按邮箱查询,如果emailLike设为true模糊查询,否则精确查询
  17. */
  18. @QueryCondition(name = "contactInfo.email", ifTrueUseLike = "emailLike")
  19. private String email;
  20. /**
  21. * 按手机号查询,如果phoneLike设为true模糊查询,否则精确查询
  22. */
  23. @QueryCondition(name = "contactInfo.phone", ifTrueUseLike = "phoneLike")
  24. private String phone;
  25. private Boolean userNameLike;
  26. private Boolean emailLike;
  27. private Boolean phoneLike;
  28. /**
  29. * 按省份代码查询
  30. */
  31. @QueryCondition(name = "contactInfo.province")
  32. private Integer province;
  33. /**
  34. * 按城市代码查询
  35. */
  36. @QueryCondition(name = "contactInfo.city")
  37. private Integer city;
  38. /**
  39. * 按地区代码查询
  40. */
  41. @QueryCondition(name = "contactInfo.area")
  42. private Integer area;
  43. /**
  44. * 按小区代码查询
  45. */
  46. @QueryCondition(name = "contactInfo.district")
  47. private Integer district;
  48. @QueryCondition(name = "nearBy", type = QueryConditionType.FATCH_EAGER)
  49. private Boolean fetchNearBy = false;
  50. /**
  51. * 范围查询,该条件值小于等于注册时间
  52. */
  53. @QueryCondition(name = "baseInfo.registrationTimestamp", type = QueryConditionType.LE)
  54. private Date leRegTimestamp;
  55. /**
  56. * 范围查询,该条件值大于等于注册时间
  57. */
  58. @QueryCondition(name = "baseInfo.registrationTimestamp", type = QueryConditionType.GE)
  59. private Date geRegTimestamp;
  60. /**
  61. * 用户等级信息查询条件,当需要用关联表里的字段当查询条件时使用
  62. */
  63. private UserGradeQO userGradeQO;
  64. }

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

  1. /**
  2. * 按用户名查询,如果userNameLike设为true模糊查询,否则精确查询
  3. */
  4. @QueryCondition(name = "authInfo.userName", ifTrueUseLike = "userNameLike")
  5. @QueryConditionGroup("group1")
  6. private String userName;
  7. /**
  8. * 按邮箱查询,如果emailLike设为true模糊查询,否则精确查询
  9. */
  10. @QueryCondition(name = "contactInfo.email", ifTrueUseLike = "emailLike")
  11. @QueryConditionGroup("group1")
  12. private String email;

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

4.2.2 UserGradeQO
  1. package com.lehumall.pojo.qo;
  2. import lazypack.common.annotation.QueryCondition;
  3. import lazypack.common.annotation.QueryConfig;
  4. import lazypack.common.component.BaseQO;
  5. @QueryConfig(daoBeanId = "userGradeDao")
  6. @SuppressWarnings("serial")
  7. public class UserGradeQO extends BaseQO<Long> {
  8. /**
  9. * 按等级标题查询
  10. */
  11. @QueryCondition(name = "baseInfo.title")
  12. private String title;
  13. }

为简化演示代码,这里只设了一个根据标题查询等级的条件。

4.2.3 BaseQO
  1. package lazypack.common.component;
  2. import java.io.Serializable;
  3. import java.util.List;
  4. /**
  5. * 基础查询类 名词解释:query object 简称 qo
  6. *
  7. * @author
  8. */
  9. public class BaseQO<T> implements Serializable {
  10. private static final long serialVersionUID = 1L;
  11. /**
  12. * 别名,在进行关联查询时设置关联对象的别名
  13. */
  14. private String alias;
  15. /**
  16. * 需要筛选出来的实体字段名,可以带“.”进入组件对象的下一级属性。用于非select *查询
  17. */
  18. private String[] projectionProperties;
  19. /**
  20. * 实体ID
  21. */
  22. private T id;
  23. /**
  24. * 实体ID集合,设置后会进行in查询
  25. */
  26. private List<T> ids;
  27. // ------------------不包含的属性条件------------------
  28. /**
  29. * 不包含的ID集合,设置后会加上这些id的不等于条件,相当于not in查询
  30. */
  31. private T[] idNotIn;
  32. // ------------------状态类条件------------------
  33. // 分页条件
  34. private Integer pageNo;
  35. private Integer pageSize;
  36. /**
  37. * 是否解析QO上的注解,当注解中的条件解析方式不适合自己的查询需要时关闭,进行手工条件组装
  38. */
  39. private Boolean enableQueryAnnotation = true;
  40. }

4.3 操作命令类Command

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

... 待续 ...

4.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条件对象可复用于上述四种查询方法。

4.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. }
4.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. }
4.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. }
4.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.5 总结

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