文档章节

JeeSite 4.0 简化业务逻辑层开发

ThinkGem
 ThinkGem
发布于 2017/09/17 19:17
字数 2871
阅读 10311
收藏 6
点赞 18
评论 35

引言

对于业务逻辑层的开发重复代码很多,尽管有代码生成器,但从代码量总的来说还是比较多,所以就有了以下抽象类及工具,对一些常用操作进行封装。

对通用新增、删除、编辑、查询,代码操作进行封装简化。你只需要写你的业务逻辑代码就可以了。

对特有树状结构特有字段如(所有父级编码、所有排序号编码、是否是叶子节点、当前节点层次)进行更新,比如,通过所有父级编码可快速查询到所有子级的数据;通过所有排序号,可快速对整个树结构进行排序;通过是否叶子节点快速得知是否有下级;根据当前层次快速知道当前节点在树中的级别。

对通用数据权限进行简化封装,将颗粒度降到人员身上,支持人员与数据,角色与数据权限定制。数据权限不仅仅支持公司、部门、角色,还可以通过配置支持你的业务字段数据信息过滤,如订单的区域、内容管理的栏目、故障单类型等等。

对事务处理使用Spring事务@Transactional注解,进行方法级别的事务控制,不用单独处理事务及回滚。如配置传播行为,进行事务继承,子事务,事务回滚行为等,配置隔离级别读取未提交的数据等。

基类及接口的继承关系

TreeService -> CrudService -> QueryService -> BaseService

TreeDao -> CrudDao -> QueryDao -> BaseDao

TreeEntity -> DataEntity -> BaseEntity

QueryService查询抽象基类

/**
 * 新建实体对象
 * @return
 */
protected T newEntity();

/**
 * 新建实体对象(带一个String构造参数)
 * @return
 */
protected T newEntity(String id);

/**
 * 获取单条数据
 * @param id 主键
 * @return
 */
public T get(String id);

/**
 * 获取单条数据
 * @param entity
 * @return
 */
public T get(T entity);

/**
 * 获取单条数据,如果获取不到,则实例化一个空实体
 * @param id 主键编号
 * @param isNewRecord 如果是新记录,则验证主键编号是否存在。
 * 					     如果存在抛出ValidationException异常。
 * @return
 */
public T get(String id, boolean isNewRecord);

/**
 * 获取单条数据,如果获取不到,则实例化一个空实体(多个主键情况下调用)
 * @param pkClass 主键类型数组
 * @param pkValue 主键数据值数组
 * @param isNewRecord 如果是新记录,则验证主键编号是否存在。
 * 					     如果存在抛出ValidationException异常。
 * @return
 */
public T get(Class<?>[] pkClass, Object[] pkValue, boolean isNewRecord);

/**
 * 列表查询数据
 * @param entity
 * @return
 */
public List<T> findList(T entity);

/**
 * 分页查询数据
 * @param page 分页对象
 * @param entity
 * @return
 */
public Page<T> findPage(Page<T> page, T entity);

/**
 * 查询列表总数
 * @param entity
 * @return
 */
public long findCount(T entity);

CrudService增删改抽象基类

该类继承QueryService抽象类

/**
 * 保存数据(插入或更新)
 * @param entity
 */
@Transactional(readOnly = false)
public void save(T entity)

/**
 * 插入数据
 * @param entity
 */
@Transactional(readOnly = false)
public void insert(T entity);

/**
 * 更新数据
 * @param entity
 */
@Transactional(readOnly = false)
public void update(T entity);

/**
 * 更新状态(级联更新子节点)
 * @param entity
 */
@Transactional(readOnly = false)
public void updateStatus(T entity);

/**
 * 删除数据
 * @param entity
 */
@Transactional(readOnly = false)
public void delete(T entity);

TreeService树结构抽象基类

该类继承CrudService抽象类

/**
 * 根据父节点获取子节点最后一条记录
 */
public T getLastByParentCode(T entity);

/**
 * 保存数据(插入或更新)
 * 实现自动保存字段:所有父级编号、所有排序号、是否是叶子节点、节点的层次级别等数据
 * 实现级联更新所有子节点数据:同父级自动保存字段
 */
@Transactional(readOnly = false)
public void save(T entity);

/**
 * 更新parent_codes、tree_sorts、tree_level字段值
 */
@Transactional(readOnly = false, isolation = Isolation.READ_UNCOMMITTED) // 可读取未提交数据
private void updateParentCodes(T entity);

/**
 * 更新当前节点排序号
 */
@Transactional(readOnly = false)
public void updateTreeSort(T entity);

/**
 * 更新tree_leaf字段值
 */
@Transactional(readOnly = false, isolation = Isolation.READ_UNCOMMITTED) // 可读取未提交数据
private void updateTreeLeaf(T entity);

/**
 * 修正本表树结构的所有父级编号
 * 包含:数据修复(parentCodes、treeLeaf、treeLevel)字段
 */
@Transactional(readOnly = false) // 可读取未提交数据
public void updateFixParentCodes();

/**
 * 按父级编码修正树结构的所有父级编号
 * 包含:数据修复(parentCodes、treeLeaf、treeLevel)字段
 */
@Transactional(readOnly = false) // 可读取未提交数据
public void updateFixParentCodes(String parentCode);

/**
 * 预留接口事件,更新子节点
 * @param childEntity 当前操作节点的子节点
 * @param parentEntity 当前操作节点
 */
protected void updateChildNode(T childEntity, T parentEntity);

/**
 * 更新状态(级联更新子节点)
 * @param entity
 */
@Transactional(readOnly = false)
public void updateStatus(T entity);

/**
 * 删除数据(级联删除子节点)
 * @param entity
 */
@Transactional(readOnly = false)
public void delete(T entity);

/**
 * 将不同级别无序的树列表进行排序,前提是sourcelist每一级是有序的<br>
 * 举例如下:<br>
 * 	List<T> targetList = Lists.newArrayList();<br>
 * 	List<T> sourcelist = service.findList(category);<br>
 * 	service.execTreeSort(targetList, sourcelist, "0");<br>
 * @param sourceList 源数据列表
 * @param targetList 目标数据列表
 * @param parentCode 目标数据列表的顶级节点
 */
public void execTreeSort(List<T> sourceList, List<T> targetList, String parentCode);

/**
 * 将简单列表code,parentCode转换为嵌套列表形式code,childList[code,childList[...]]<br>
 * 举例如下:<br>
 * 	List<T> targetList = Lists.newArrayList();<br>
 * 	List<T> sourcelist = service.findList(category);<br>
 * 	service.execChildListBulid(targetList, sourcelist, "0");<br>
 * @param sourceList 源数据列表
 * @param targetList 目标数据列表
 * @param parentCode 目标数据列表的顶级节点
 */
public void execChildListBulid(List<T> sourceList, List<T> targetList, String parentCode);

数据权限调用

本次对数据权限这块进行了全面的升级,让数据权限颗粒度细化到人员身上,支持人员与权限和角色与权限。

调用示例:

/**
 * 添加数据权限过滤条件
 */
public void addDataScopeFilter(T entity){
	// 举例1:公司数据权限过滤,实体类@Table注解extWhereKeys="dsf"
	company.getSqlMap().getDataScope().addFilter("dsf", "Company", "a.company_code");
	// 举例2:部门数据权限过滤,实体类@Table注解extWhereKeys="dsf"
	office.getSqlMap().getDataScope().addFilter("dsf", "Office", "a.office_code");
	// 举例3:角色数据权限过滤,实体类@Table注解extWhereKeys="dsf"
	role.getSqlMap().getDataScope().addFilter("dsf", "Role", "a.role_code");
	// 举例4:用户、员工数据权限根据部门过滤,实体类@Table注解extWhereKeys="dsfOffice"
	empUser.getSqlMap().getDataScope().addFilter("dsfOffice",
			"Office", "e.office_code", "a.create_by");
	// 举例5:用户、员工数据权限根据公司过滤,实体类@Table注解extWhereKeys="dsfCompany"
	empUser.getSqlMap().getDataScope().addFilter("dsfCompany",
			"Company", "e.company_code", "a.create_by");
}

API接口:

/**
 * 数据范围过滤
 * @param sqlMapKey sqlMap的键值,举例:如设置“dsf”数据范围过滤,则:<br>
 * 		exists方式对应:sqlMap.dsf;   join方式对应:sqlMap.dsfForm 和 sqlMap.dsfWhere
 * @param ctrlTypes 控制类型,多个用“,”隔开,举例:<br>
 *		控制角色:Role<br>
 *		控制部门:Office<br>
 *		控制公司:Company<br>
 * @param bizCtrlDataFields 业务表对应过滤表别名或需要过滤的表别名加权限字段,多个使用“,”分隔,
 * 		长度必须与tableTypes保持一致,举例:<br>
 * 		业务表控制角色:a.role_code<br>
 * 		业务表控制部门:a.office_code<br>
 * 		业务表控制公司:a.company_code<br>
 * @param bizCtrlUserField 业务表对应表别名或用户字段。过滤只可以查看本人数据。
 * 		不设置的话,如果没有范围权限,则查不到任何数据,举例:<br>
 * 		业务表a.create_by"
 * @example
 * 		1)在Service中调用如下两种方式:<br>
 * 			// 添加数据权限过滤条件(控制角色)<br>
 * 			entity.getSqlMap().getDataScope().addFilter("dsf", "Role", "a.role_code");<br>
 * 			// 添加数据权限过滤条件(控制公司和部门)<br>
 * 			entity.getSqlMap().getDataScope().addFilter("dsf",<br>
 * 				"Office,Company", "a.office_code,a.company_code");<br>
 * 		2)MyBatis \@Table 注解中调用如下:<br>
 * 			采用 EXISTS方式调用	: \@Table(extWhereKeys="dsf")
 * 			采用JOIN方式调用	: \@Table(extFromKeys="dsf",extWhereKeys="dsf")
 * 		3)MyBatis Mapper 中调用如下两种方式:<br>
 * 			采用 EXISTS方式调用	: 将  ${sqlMap.dsf} 放在Where后<br>
 * 			采用JOIN方式调用	: 将  ${sqlMap.dsfFrom} 放在Form后 ,将  ${sqlMap.dsfWhere} 放在Where后<br>
 */
public QueryDataScope addFilter(String sqlMapKey, String ctrlTypes, String bizCtrlDataFields, String bizCtrlUserField);

数据库事务

事务管理对于企业应用来说是至关重要的,当出现异常情况,它也可以保证数据的一致性。

JeeSite主要使用Spring的@Transactional注解,也称声明式事务管理,是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需通过基于@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。

注解属性

属性类型描述
传播性枚举型可选的传播性设置(默认值:Propagation.REQUIRED)
隔离性枚举型可选的隔离性级别(默认值:Isolation.ISOLATION_DEFAULT)
只读性布尔型读写型事务 vs. 只读型事务
超时int型事务超时(以秒为单位)
回滚异常类(rollbackFor)Class 类的实例,必须是Throwable 的子类异常类,遇到时必须进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。
不回滚异常类(noRollbackFor)Class 类的实例,必须是Throwable的子类异常类,遇到时必须不回滚。

事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在Propagation定义中包括了如下几个表示传播行为的常量:

  • Propagation.REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务,这是默认值。
  • Propagation.REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • Propagation.NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。Isolation 接口中定义了五个表示隔离级别的常量:

  • Isolation.DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是Isolation.READ_COMMITTED。
  • Isolation.READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • Isolation.READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • Isolation.REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • Isolation.SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

© 著作权归作者所有

共有 人打赏支持
ThinkGem

ThinkGem

粉丝 851
博文 135
码字总数 20086
作品 1
济南
架构师
加载中

评论(35)

达亚达2

引用来自“达亚达2”的评论

群主辛苦了,等你产品发布之日,我立马捐赠1000RMB。我的升职加薪全靠群主了

引用来自“thinkgem”的评论

谢谢支持,谢谢捧场

引用来自“达亚达2”的评论

GEM也可以考虑出一本书,构建系统方面的,一些心得等等

引用来自“thinkgem”的评论

自认为还达不到那个高度:smile:
就围绕jeesite4.0以及以后的分布式,就是一本很不错的书了,使用jeesite的用户也受益匪浅了。
ThinkGem
ThinkGem

引用来自“达亚达2”的评论

群主辛苦了,等你产品发布之日,我立马捐赠1000RMB。我的升职加薪全靠群主了

引用来自“thinkgem”的评论

谢谢支持,谢谢捧场

引用来自“达亚达2”的评论

GEM也可以考虑出一本书,构建系统方面的,一些心得等等
自认为还达不到那个高度:smile:
达亚达2

引用来自“达亚达2”的评论

群主辛苦了,等你产品发布之日,我立马捐赠1000RMB。我的升职加薪全靠群主了

引用来自“thinkgem”的评论

谢谢支持,谢谢捧场
GEM也可以考虑出一本书,构建系统方面的,一些心得等等
长沙大东家
长沙大东家

引用来自“thinkgem”的评论

引用来自“长沙大东家”的评论

建议将表格集成为ag-grid,另外,尽量提供前端的vuejs2.0版本。
界面上iview,element这种比较可以的。
暂时没有考虑前后分离的模式,还是采用经典开发模式比较易学易懂。

回复@thinkgem : 那也可以,可以建议把ag-grid放里面不?
ThinkGem
ThinkGem

引用来自“长沙大东家”的评论

建议将表格集成为ag-grid,另外,尽量提供前端的vuejs2.0版本。
界面上iview,element这种比较可以的。
暂时没有考虑前后分离的模式,还是采用经典开发模式比较易学易懂。
ThinkGem
ThinkGem

引用来自“yun_88416425”的评论

群主QQ多少
可以加qq群找到我,在jeesite.com上有群号
ThinkGem
ThinkGem

引用来自“南京大猫”的评论

7年左右Java开发经验,可以加入开发吗?
可以加qq群找到我,在jeesite.com上有群号
ThinkGem
ThinkGem

引用来自“达亚达2”的评论

群主辛苦了,等你产品发布之日,我立马捐赠1000RMB。我的升职加薪全靠群主了
谢谢支持,谢谢捧场
y
yun_88416425
群主QQ多少
jeesite 快速开发平台 初体验

http://www.jeesite.com/ GitHub:https://github.com/thinkgem/jeesite 开源中国:http://git.oschina.net/thinkgem/jeesite 更多文档 https://github.com/thinkgem/jeesite/tree/master/do......

晨猫 ⋅ 05/21 ⋅ 0

JeeSite环境搭建及运行和打包(master20161117)

涉及的软件: 1、phpStudy(主要用MySql) 2、maven3(用于依赖包,下面我将上传已经下载好所有依赖包的版本,保证运行正常) 具体操作: 0、前言 由于GitHub上的Release版本没有及时更新,所...

easonjim ⋅ 2016/11/18 ⋅ 0

JeeSite4.0,一直报这个问题,求解释

DEBUG [com.jeesite.common.io.PropertiesUtils] - Loading jeesite config: [classpath:/config/jeesite-core.yml, classpath:config/jeesite.yml, classpath:config/application.yml, clas......

陈豫 ⋅ 05/08 ⋅ 0

JeeSite 4.0.3 发布,企业级快速开发平台

新增 新增:core项目增加单元测试支持类 ApplicationTest.java 新增:代码生成config.xml支持自定义,放同目录下config-custom.xml文件即可覆盖 新增:shiro.allowRequestMethods 参数,可指...

ThinkGem ⋅ 05/30 ⋅ 0

jeesite访问页面时不存在

生成代码时把包路径com.thinkgem.jeesite.modules改为自己定义的,访问不了页面,我把spring-context.xml中和和扫描basePackage下所有以@MyBatisDao注解的接口都添加了自己定义的包,是不是还有...

nicele ⋅ 04/19 ⋅ 0

JeeSite 4.0.2 发布,企业级快速开发平台

新增 新增:支持分布式事务,多数据源下事务支持 新增:支持MyBatisDao注解指定数据源名称,Dao层动态切换数据源 新增:国际化底层框架、通用组件、机构管理功能和代码生成模板 新增:Linux...

ThinkGem ⋅ 04/22 ⋅ 0

jeesite 扩展手机验证和密码验证

$.validator.addMethod("mobile",function(value,element,params){

wangxujun59 ⋅ 06/12 ⋅ 0

jeesite v4.0.2多数据源配置问题

现在项目管理后台想用jeesite4.0,需要支持多数据源配置。自己配置了一天也不好使。请问有做过类似的同学分享下经验。感激不尽。最好能有详细的配置步骤。谢谢了

crazyYXD ⋅ 05/04 ⋅ 0

解析 Kindle 剪贴文件中的笔记和标记 - clippings-gem

Clippings Clippings 能够解析 Kindle 剪贴文件中的笔记和标记。 安装 Add this line to your application's Gemfile: gem 'clippings' And then execute: $ bundle Or install it yourself......

杜小豆 ⋅ 03/02 ⋅ 0

企业信息化快速开发平台--JeeSite

JeeSite是基于多个优秀的开源项目,高度整合封装而成的高效,高性能,强安全性的 开源 Java EE快速开发平台。 JeeSite本身是以Spring Framework为核心容器,Spring MVC为模型视图控制器,MyB...

thinkgem ⋅ 2013/02/24 ⋅ 49

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

Nginx + uwsgi @ubuntu

uwsgi 安装 sudo apt-get install python3-pip # 注意 ubuntu python3默认没有安装pippython3 -m pip install uwsgi 代码(test.py) def application(env, start_response): start_res......

袁祾 ⋅ 昨天 ⋅ 0

版本控制工具

CSV , SVN , GIT ,VSS

颖伙虫 ⋅ 昨天 ⋅ 0

【2018.06.19学习笔记】【linux高级知识 13.1-13.3】

13.1 设置更改root密码 13.2 连接mysql 13.3 mysql常用命令

lgsxp ⋅ 昨天 ⋅ 0

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部