SPED是一个强大的开发工具,SPED通过编写业务描述文档,根据文档构建业务系统。
本文以用户资源权限为主,演示SPED快速神奇的开发过程。
权限管理和用户操作记录是业务系统中常用的功能,本文以这些功能的开发实现为例介绍SPED开发过程。下面是本案例E-R图(实体关系图):
权限管理中涉及资源、角色、用户、用户组4个核心的实体类,其中,资源只能分配给角色,资源与角色之间是一个多对多的关系,然后通过将角色分配给用户或用户组,建立起用户与资源的分配关系。通常情况下,系统管理员会关心以下需求:
本文从开发者的视角出发,分为以下几个部分:
图1 E-R实体关系
权限管理中涉及资源、角色、用户、用户组4个核心的实体类,其中,资源只能分配给角色,资源与角色之间是一个多对多的关系,然后通过将角色分配给用户或用户组,建立起用户与资源的分配关系。通常情况下,系统管理员会关心以下需求:
●新建用户、用户组、角色
●权限分配
●查询特定用户或用户组具有哪些角色
●查询特定用户或用户组可以访问哪些资源
●查询特定角色被分配给哪些用户或用户组
●查询特定资源可以被哪些用户或用户组访问
●查询特定资源被分配给哪些角色
●查询特定用户被划分到哪些用户组
●查询用户登录、访问情况
本文从开发者的视角出发,分为以下几个部分:
实体类描述
实体设计可以使用SPED描述实现,也可以在集成开发环境(IDE)中实现,本文采用SPED描述。展开查看SPED实体类描述
package sped;
// 下面是关于实体类的SPED描述
bean {
src = demo; // POJO源文件夹是工程下的demo目录
pkg = com.admin; // POJO的package是com.admin
User:用户实体类{
pkg:.user; // User类的package是com.admin.user。
fl:[
id: int:编号:-1, // 用户编号类型int,初始值为-1
name::用户姓名,
userName::用户名,
email::电子邮箱,
pwd::登陆密码,
mobilephone::手机号码,
telephone::电话号码,
portrait::用户头像,
regTime::注册时间,
activate::是否激活:N, // 初始值为N,Y表示已经激活,N表示没有激活
activatedTime::激活时间,
status::有效状态:Y,
];
}
Resource:资源实体类{
pkg:.resource;
fl:[
id::资源编号,
url::请求url,
name::资源名称,
actionClass:Class<?>:处理请求的类,
method:java.lang.reflect.Method:处理请求的方法,
];
}
Role:角色实体类{
pkg:.auth;
fl:[id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
UserGroup:用户组实体类{
pkg:.auth;
fl:[
id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
ResourceRoleMapping:角色与资源的映射{
pkg:.auth;
fl:[
id::编号,
resourceId::资源编号,
roleId::角色编号,
];
}
RoleUserMapping:角色与用户的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
userId::人员编号,
];
}
RoleGroupMapping:角色与组的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
groupId::组编号,
];
}
GroupUserMapping:用户组与用户的映射{
pkg:.auth;
fl:[
id::编号,
groupId::组编号,
userId::人员编号,
];
}
UserOperation:用户操作记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::记录时间,
host::用户IP,
url::访问的URL,
action::操作对象,
actionState::操作结果或状态,
sessionId::浏览器sessionid,
];
}
UserLogin:用户登录记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
time::登陆时间,
userName::用户名,
host::用户IP地址,
];
}
UserDownload:用户下载记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::下载时间:,
fileName::下载文件,
];
}
}
// 下面是关于实体类的SPED描述
bean {
src = demo; // POJO源文件夹是工程下的demo目录
pkg = com.admin; // POJO的package是com.admin
User:用户实体类{
pkg:.user; // User类的package是com.admin.user。
fl:[
id: int:编号:-1, // 用户编号类型int,初始值为-1
name::用户姓名,
userName::用户名,
email::电子邮箱,
pwd::登陆密码,
mobilephone::手机号码,
telephone::电话号码,
portrait::用户头像,
regTime::注册时间,
activate::是否激活:N, // 初始值为N,Y表示已经激活,N表示没有激活
activatedTime::激活时间,
status::有效状态:Y,
];
}
Resource:资源实体类{
pkg:.resource;
fl:[
id::资源编号,
url::请求url,
name::资源名称,
actionClass:Class<?>:处理请求的类,
method:java.lang.reflect.Method:处理请求的方法,
];
}
Role:角色实体类{
pkg:.auth;
fl:[id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
UserGroup:用户组实体类{
pkg:.auth;
fl:[
id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
ResourceRoleMapping:角色与资源的映射{
pkg:.auth;
fl:[
id::编号,
resourceId::资源编号,
roleId::角色编号,
];
}
RoleUserMapping:角色与用户的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
userId::人员编号,
];
}
RoleGroupMapping:角色与组的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
groupId::组编号,
];
}
GroupUserMapping:用户组与用户的映射{
pkg:.auth;
fl:[
id::编号,
groupId::组编号,
userId::人员编号,
];
}
UserOperation:用户操作记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::记录时间,
host::用户IP,
url::访问的URL,
action::操作对象,
actionState::操作结果或状态,
sessionId::浏览器sessionid,
];
}
UserLogin:用户登录记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
time::登陆时间,
userName::用户名,
host::用户IP地址,
];
}
UserDownload:用户下载记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::下载时间:,
fileName::下载文件,
];
}
}
package sped;
mapping {
// User和所有拥有userId属性的类存在一对多的关系,关系的主外属性分别是id和userId
{ fb:User; tb:*; ff:id; tf:userId; }
// UserGroup和所有拥有groupId属性的类存在一对多的关系,关系的主外属性分别是id和groupId
{ fb:UserGroup; tb:*; ff:id; tf:groupId; }
{ fb:Role; tb:*; ff:id; tf:roleId; }
{ fb:Resource; tb:*; ff:id; tf:resourceId; }
// 下面是多对多关系描述
// User和UserGroup存在多对多关系,mb:*,表示SPED从所有类中寻找拥有userId和groupId两个属性的类作为映射
{ fb:User; tb:UserGroup; ff:id; tf:id; mb:*; fm:userId-groupId; }
// User和Role存在多对多关系,mb:*,表示SPED从所有类中寻找拥有userId和roleId两个属性的类作为映射
{ fb:User; tb:Role; ff:id; tf:id; mb:*; fm:userId-roleId; }
{ fb:UserGroup; tb:Role; ff:id; tf:id; mb:*; fm:groupId-roleId; }
{ fb:Resource; tb:Role; ff:id; tf:id; mb:*; fm:resourceId-roleId; }
}
mapping {
// User和所有拥有userId属性的类存在一对多的关系,关系的主外属性分别是id和userId
{ fb:User; tb:*; ff:id; tf:userId; }
// UserGroup和所有拥有groupId属性的类存在一对多的关系,关系的主外属性分别是id和groupId
{ fb:UserGroup; tb:*; ff:id; tf:groupId; }
{ fb:Role; tb:*; ff:id; tf:roleId; }
{ fb:Resource; tb:*; ff:id; tf:resourceId; }
// 下面是多对多关系描述
// User和UserGroup存在多对多关系,mb:*,表示SPED从所有类中寻找拥有userId和groupId两个属性的类作为映射
{ fb:User; tb:UserGroup; ff:id; tf:id; mb:*; fm:userId-groupId; }
// User和Role存在多对多关系,mb:*,表示SPED从所有类中寻找拥有userId和roleId两个属性的类作为映射
{ fb:User; tb:Role; ff:id; tf:id; mb:*; fm:userId-roleId; }
{ fb:UserGroup; tb:Role; ff:id; tf:id; mb:*; fm:groupId-roleId; }
{ fb:Resource; tb:Role; ff:id; tf:id; mb:*; fm:resourceId-roleId; }
}
package sped;
db = sqlite; // 使用嵌入式数据库SQLite
ctm = %user%:demo_user_&name-l, // 类全名带user字样的POJO数据表为“demo_user_类名小写”,下同
%resource%:demo_res_&name-l,
%auth%:demo_auth_&name-l,
%record%:demo_record_&name-l;
fcm = *(*c_&name-l); // 所有POJO的所有属性的字段名为“c_属性名小写”
cc = config.DemoUtil.getConnection();
igf = Resource:[method,actionClass];
table{
psql = true; // 是否打印创建数据表的SQL,缺省为true
drop = true; // 是否删除同名数据表,缺省为false
*{ l:100; } // 所有字段长度为100
id{ l:100; c:PRIMARY KEY; } // id的字段作为数据表的主键
fileName,url{ l:2000; } // fileName和url的字段长度为2000
}
db = sqlite; // 使用嵌入式数据库SQLite
ctm = %user%:demo_user_&name-l, // 类全名带user字样的POJO数据表为“demo_user_类名小写”,下同
%resource%:demo_res_&name-l,
%auth%:demo_auth_&name-l,
%record%:demo_record_&name-l;
fcm = *(*c_&name-l); // 所有POJO的所有属性的字段名为“c_属性名小写”
cc = config.DemoUtil.getConnection();
igf = Resource:[method,actionClass];
table{
psql = true; // 是否打印创建数据表的SQL,缺省为true
drop = true; // 是否删除同名数据表,缺省为false
*{ l:100; } // 所有字段长度为100
id{ l:100; c:PRIMARY KEY; } // id的字段作为数据表的主键
fileName,url{ l:2000; } // fileName和url的字段长度为2000
}
package sped;
db = sqlite;
ctm = %user%:demo_user_&name-l, // 类全名带user字样的POJO数据表为“demo_user_类名小写”,下同
%resource%:demo_res_&name-l,
%auth%:demo_auth_&name-l,
%record%:demo_record_&name-l;
fcm = *(*c_&name-l); // 所有POJO的所有属性的字段名为“c_属性名小写”
cover = true; // 新的源代码是否覆盖旧的源代码,缺省为false
osf = demo; // Object Source Folder,POJO的源文件夹是demo
tsf = demo; // Generate To Source Folder,存放DAO相关程序的源文件夹也是demo
cc = config.DemoUtil.getConnection(); // JDBC数据库连接代码
igf = Resource:[method,actionClass]; // Ignore Field,忽略Resource中method和actionClass属性
dao {
pkf = *id; // pkf变量,所有POJO的主键属性是id(没有id属性的POJO例外)。
dft = com.admin.factory.DAOFactory; // dft变量,声明工厂类
{ md: c,p,i,bi,m,e; } // 每个POJO都生成代号为c、p、i、bi、m、e对应的DAO方法
id{ md: ga,da,d,bu,u,f; } // 具有id属性的POJO都生成代号为ga、da、d、bu、u、f对应的DAO方法
%Id{ md: g,ga,fp,fc; } // 具有属性名以Id结尾的属性的POJO都生成代号为g、ga、fp、fc对应的DAO方法
status{ md: g; } // 具有status属性的POJO都生成代号为g对应的DAO方法
}
db = sqlite;
ctm = %user%:demo_user_&name-l, // 类全名带user字样的POJO数据表为“demo_user_类名小写”,下同
%resource%:demo_res_&name-l,
%auth%:demo_auth_&name-l,
%record%:demo_record_&name-l;
fcm = *(*c_&name-l); // 所有POJO的所有属性的字段名为“c_属性名小写”
cover = true; // 新的源代码是否覆盖旧的源代码,缺省为false
osf = demo; // Object Source Folder,POJO的源文件夹是demo
tsf = demo; // Generate To Source Folder,存放DAO相关程序的源文件夹也是demo
cc = config.DemoUtil.getConnection(); // JDBC数据库连接代码
igf = Resource:[method,actionClass]; // Ignore Field,忽略Resource中method和actionClass属性
dao {
pkf = *id; // pkf变量,所有POJO的主键属性是id(没有id属性的POJO例外)。
dft = com.admin.factory.DAOFactory; // dft变量,声明工厂类
{ md: c,p,i,bi,m,e; } // 每个POJO都生成代号为c、p、i、bi、m、e对应的DAO方法
id{ md: ga,da,d,bu,u,f; } // 具有id属性的POJO都生成代号为ga、da、d、bu、u、f对应的DAO方法
%Id{ md: g,ga,fp,fc; } // 具有属性名以Id结尾的属性的POJO都生成代号为g、ga、fp、fc对应的DAO方法
status{ md: g; } // 具有status属性的POJO都生成代号为g对应的DAO方法
}
将以上各个部分的SPED描述汇总到同一个文件中,如demo1.sped,然后将该文件放到项目的sped目录下,然后执行org.sped.YUNSOFT_ORG_RUN类中的main方法,如下图:
在SPED主窗口中选择需要执行的SPED描述文件,然后,点击“执行”,执行完后刷新项目(通常按F5刷新),将会得到如下图的结果:
下面通过编写测试代码的方式对SPED开发的结果进行演示,测试代码如下:
图2 执行SPED业务描述
在SPED主窗口中选择需要执行的SPED描述文件,然后,点击“执行”,执行完后刷新项目(通常按F5刷新),将会得到如下图的结果:
图3 SPED执行结果
下面通过编写测试代码的方式对SPED开发的结果进行演示,测试代码如下:
package com.admin;
import java.util.ArrayList;
import java.util.List;
import com.admin.auth.Role;
import com.admin.auth.UserGroup;
import com.admin.resource.Resource;
import com.admin.user.User;
public class DemoTest {
/**
* 添加角色
* @return boolean
* @throws Exception
*/
public boolean roleInsert() throws Exception{
Role role = new Role();
role.setId( "r0001");
role.setName( "测试角色");
role.setStatus( "Y"); // Y表示角色有效
return role.insert();
}
/**
* 以角色为主,查询角色拥有哪些资源的访问权限
* @param roleId 角色编号
* @return Resource[]
* @throws Exception
*/
public Resource[] getResourcesByRole(String roleId) throws Exception{
Role role = new Role();
role.setId(roleId);
return role.toResources();
}
/**
* 以资源为主,查询资源被分配给哪些角色
* @param resourceId 资源编号
* @return Role[]
* @throws Exception
*/
public Role[] getRolesByResource(String resourceId) throws Exception{
Resource resource = new Resource();
resource.setId(resourceId);
return resource.toRoles();
}
/**
* 以用户为主,查询用户拥有的所有角色,
* 这些角色包括与该用户相关的用户组拥有的角色。
* @param userId 用户编号
* @return Role[]
* @throws Exception
*/
public Role[] getRolesByUser( int userId) throws Exception{
List<Role> list = new ArrayList<Role>();
User user = new User();
user.setId(userId);
// 查询直接分配给用户的角色
Role[] userRoles = user.toRoles();
if( null != userRoles && userRoles.length > 0){
int size = userRoles.length;
for ( int i = 0; i < size; i++) {
list.add(userRoles[i]);
}
}
// 查询用户所属的用户组
UserGroup[] userGroups = user.toUserGroups();
if( null == userGroups || userGroups.length < 1){
return list.toArray( new Role[list.size()]);
}
int size = userGroups.length;
for ( int i = 0; i < size; i++) {
// 查询用户组拥有的角色
Role[] groupRoles = userGroups[i].toRoles();
if( null != groupRoles && groupRoles.length > 0){
for ( int j = 0; j < groupRoles.length; j++) {
list.add(groupRoles[j]);
}
}
}
return list.toArray( new Role[list.size()]);
}
/**
* 以角色为主,查询角色被分配给哪些用户,
* 这些用户包括与该角色相关的用户组中存在的用户
* @param roleId 角色编号
* @return User[]
* @throws Exception
*/
public User[] getUsersByRole(String roleId) throws Exception{
List<User> list = new ArrayList<User>();
Role role = new Role();
role.setId(roleId);
// 查询资源被直接分配给哪些用户
User[] roleUsers = role.toUsers();
if( null != roleUsers && roleUsers.length > 0){
int size = roleUsers.length;
for ( int i = 0; i < size; i++) {
list.add(roleUsers[i]);
}
}
// 查询资源被分配给哪些用户组
UserGroup[] roleGroups = role.toUserGroups();
if( null == roleGroups || roleGroups.length < 1){
return list.toArray( new User[list.size()]);
}
int size = roleGroups.length;
for ( int i = 0; i < size; i++) {
// 查询用户组中存在哪些用户
User[] groupUsers = roleGroups[i].toUsers();
if( null != groupUsers && groupUsers.length > 0){
for ( int j = 0; j < groupUsers.length; j++) {
list.add(groupUsers[j]);
}
}
}
return list.toArray( new User[list.size()]);
}
/**
* 通过用户查询用户的所有登录日志
* @param userId 用户编号
* @return UserLogin[]
* @throws Exception
*/
public UserLogin[] getUserLoginsByUser( int userId) throws Exception{
User user = new User();
user.setId(userId);
return user.toUserLogins();
}
}
import java.util.ArrayList;
import java.util.List;
import com.admin.auth.Role;
import com.admin.auth.UserGroup;
import com.admin.resource.Resource;
import com.admin.user.User;
public class DemoTest {
/**
* 添加角色
* @return boolean
* @throws Exception
*/
public boolean roleInsert() throws Exception{
Role role = new Role();
role.setId( "r0001");
role.setName( "测试角色");
role.setStatus( "Y"); // Y表示角色有效
return role.insert();
}
/**
* 以角色为主,查询角色拥有哪些资源的访问权限
* @param roleId 角色编号
* @return Resource[]
* @throws Exception
*/
public Resource[] getResourcesByRole(String roleId) throws Exception{
Role role = new Role();
role.setId(roleId);
return role.toResources();
}
/**
* 以资源为主,查询资源被分配给哪些角色
* @param resourceId 资源编号
* @return Role[]
* @throws Exception
*/
public Role[] getRolesByResource(String resourceId) throws Exception{
Resource resource = new Resource();
resource.setId(resourceId);
return resource.toRoles();
}
/**
* 以用户为主,查询用户拥有的所有角色,
* 这些角色包括与该用户相关的用户组拥有的角色。
* @param userId 用户编号
* @return Role[]
* @throws Exception
*/
public Role[] getRolesByUser( int userId) throws Exception{
List<Role> list = new ArrayList<Role>();
User user = new User();
user.setId(userId);
// 查询直接分配给用户的角色
Role[] userRoles = user.toRoles();
if( null != userRoles && userRoles.length > 0){
int size = userRoles.length;
for ( int i = 0; i < size; i++) {
list.add(userRoles[i]);
}
}
// 查询用户所属的用户组
UserGroup[] userGroups = user.toUserGroups();
if( null == userGroups || userGroups.length < 1){
return list.toArray( new Role[list.size()]);
}
int size = userGroups.length;
for ( int i = 0; i < size; i++) {
// 查询用户组拥有的角色
Role[] groupRoles = userGroups[i].toRoles();
if( null != groupRoles && groupRoles.length > 0){
for ( int j = 0; j < groupRoles.length; j++) {
list.add(groupRoles[j]);
}
}
}
return list.toArray( new Role[list.size()]);
}
/**
* 以角色为主,查询角色被分配给哪些用户,
* 这些用户包括与该角色相关的用户组中存在的用户
* @param roleId 角色编号
* @return User[]
* @throws Exception
*/
public User[] getUsersByRole(String roleId) throws Exception{
List<User> list = new ArrayList<User>();
Role role = new Role();
role.setId(roleId);
// 查询资源被直接分配给哪些用户
User[] roleUsers = role.toUsers();
if( null != roleUsers && roleUsers.length > 0){
int size = roleUsers.length;
for ( int i = 0; i < size; i++) {
list.add(roleUsers[i]);
}
}
// 查询资源被分配给哪些用户组
UserGroup[] roleGroups = role.toUserGroups();
if( null == roleGroups || roleGroups.length < 1){
return list.toArray( new User[list.size()]);
}
int size = roleGroups.length;
for ( int i = 0; i < size; i++) {
// 查询用户组中存在哪些用户
User[] groupUsers = roleGroups[i].toUsers();
if( null != groupUsers && groupUsers.length > 0){
for ( int j = 0; j < groupUsers.length; j++) {
list.add(groupUsers[j]);
}
}
}
return list.toArray( new User[list.size()]);
}
/**
* 通过用户查询用户的所有登录日志
* @param userId 用户编号
* @return UserLogin[]
* @throws Exception
*/
public UserLogin[] getUserLoginsByUser( int userId) throws Exception{
User user = new User();
user.setId(userId);
return user.toUserLogins();
}
}
package sped;
/**
* bean模块,用于定义JAVA业务实体类(POJO,下同)。
* bean模块执行完后,bean模块中定义的所有POJO会自动导入到pojo变量(pojo容器)中,
* bean模块不是必须的,POJO的定义可以在集成开发环境(IDE,如Eclipse)中进行,然后
* 将在IDE中创建的POJO存放到pojo变量中,详情参见pojo变量。
*
*
* bean模块内部用到的主要变量:
* en变量:Enable,用于是否启用该模块,缺省为true,若为false则模块被忽略。
*
* src变量:描述POJO存放的源文件夹,缺省为src,告诉bean模块将生成的POJO源代码存
* 放到哪里,如src = demo。
*
* pkg变量:描述POJO默认package,如“pkg = com.admin;”。
*
* cmf变量:Class Modify,类修饰,描述所有类的默认修饰,缺省为public,
* 如“cmf = public final;”,表示所有类将声明为final。
*
* fmf变量:Field Modify,属性修饰,描述所有属性的默认修饰,缺省为private,
* 如“fmf = public;”,表示所有属性被声明为可以被直接访问的public属性。
*
* fdt变量:Field Default Type,属性默认类型,缺省为String,如“fdt = int;”,
* 表示在定义POJO属性的时,当类型声明为空,那么该属性的类型为int。
*
*
* POJO定义方式:
* 类名:类描述{ fl:[属性,....,属性]; }
* 类名如果包含package(类全名,下同),那么pkg变量和pkg关键字都失效;
* 类描述不是必须的,如果有类描述,将会生成类的注释;
* fl是FieldList(属性列表)的简写。
*
*
* POJO定义中用到的主要关键字:
* fl关键字:Field List,属性列表。
*
* src关键字:特别指定该POJO存放的源文件夹,src关键字将覆盖src变量。
*
* pkg关键字:特别指定该POJO的package,如果src关键字以“.”开头,那么该POJO的package
* 为pkg变量的值加上pkg关键字的值,否则,pkg关键字将覆盖pkg变量。
*
* cmf关键字:特定指定该POJO修饰,cmf关键字将覆盖cmf变量。
*
* fmf关键字:特定指定该POJO的属性的修饰,fmf关键字将覆盖fmf变量。
*
* fdt关键字:特定指定该POJO的属性的默认类型,fdt关键字将覆盖fdt变量。
*
*
* POJO属性定义方式:
* 属性名:属性类型:属性描述:属性初始值
* 属性类型声明在属性名后第一个“:”后面,若为空,属性类型为fdt关键字或fdt变量的值;
* 属性描述不是必须的,出现在属性名后第二个“:”后面,将作为属性的注释;
* 属性初始值出现在属性名后第三个“:”后面。
*
*/
bean {
src = demo; // POJO源文件夹是工程下的demo目录
pkg = com.admin; // POJO的package是com.admin
User:用户实体类{
pkg:.user; // User类的package是com.admin.user。
fl:[
id: int:编号:-1, // 用户编号类型int,初始值为-1
name::用户姓名,
userName::用户名,
email::电子邮箱,
pwd::登陆密码,
mobilephone::手机号码,
telephone::电话号码,
portrait::用户头像,
regTime::注册时间,
activate::是否激活:N, // 初始值为N,Y表示已经激活,N表示没有激活
activatedTime::激活时间,
status::有效状态:Y,
];
}
Resource:资源实体类{
pkg:.resource;
fl:[
id::资源编号,
url::请求url,
name::资源名称,
actionClass:Class<?>:处理请求的类,
method:java.lang.reflect.Method:处理请求的方法,
];
}
Role:角色实体类{
pkg:.auth;
fl:[id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
UserGroup:用户组实体类{
pkg:.auth;
fl:[
id::编号,
name::名称,
status::有效状态:Y, // 初始值为Y,Y表示有效,N表示无效
];
}
ResourceRoleMapping:角色与资源的映射{
pkg:.auth;
fl:[
id::编号,
resourceId::资源编号,
roleId::角色编号,
];
}
RoleUserMapping:角色与用户的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
userId::人员编号,
];
}
RoleGroupMapping:角色与组的映射{
pkg:.auth;
fl:[
id::编号,
roleId::角色编号,
groupId::组编号,
];
}
GroupUserMapping:用户组与用户的映射{
pkg:.auth;
fl:[
id::编号,
groupId::组编号,
userId::人员编号,
];
}
UserOperation:用户操作记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::记录时间,
host::用户IP,
url::访问的URL,
action::操作对象,
actionState::操作结果或状态,
sessionId::浏览器sessionid,
];
}
UserLogin:用户登录记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
time::登陆时间,
userName::用户名,
host::用户IP地址,
];
}
UserDownload:用户下载记录实体类{
pkg:.record;
fl:[
id::记录编号,
userId::用户编号:-1,
userName::用户名,
time::下载时间:,
fileName::下载文件,
];
}
}
/**
* mapping模块,描述POJO业务实体之间的关系。
*
* 一对多或多对一关系定义方式:
* { fb:POJO; tb:POJO; ff:属性; tf:属性; }
* fb关键字:From Bean,关系的主POJO。
* tb关键字:To Bean,被关系的POJO。
* ff关键字:From Field,关系的主属性。
* tf关键字:To Field,被关系属性。
*
* 多对多关系定义方式:
* { fb:POJO; tb:POJO; ff:属性; tf:属性; mb:映射实体类; fm:属性映射}
* fb关键字:From Bean,关系的主POJO。
* tb关键字:To Bean,被关系的POJO。
* ff关键字:From Field,关系的主属性。
* tf关键字:To Field,被关系属性。
* mb关键字:Mapping Bean,映射实体类。
* fm关键字:File Mapping,包含映射两个属性,用“-”隔开,如属性A-属性B,
* ff关键字代表的属性与属性A的关系属于主外键关系,tf关键字代
* 表的属性与属性B的关系也属于主外键关系。属性A与属性B必须都
* 属于映射实体类的属性。
*
*/
mapping {
{ fb:User; tb:*; ff:id; tf:userId; }
{ fb:UserGroup; tb:*; ff:id; tf:groupId; }
{ fb:Role; tb:*; ff:id; tf:roleId; }
{ fb:Resource; tb:*; ff:id; tf:resourceId; }
{ fb:User; tb:UserGroup; ff:id; tf:id; mb:*; fm:userId-groupId; }
{ fb:User; tb:Role; ff:id; tf:id; mb:*; fm:userId-roleId; }
{ fb:UserGroup; tb:Role; ff:id; tf:id; mb:*; fm:groupId-roleId; }
{ fb:Resource; tb:Role; ff:id; tf:id; mb:*; fm:resourceId-roleId; }
}
/**
*
* 以下变量主要用于table模块和dao模块,这些变量通常声明在模块之外。
*
* pojo变量:POJO容器,告诉SPED所有模块的作用域是哪些POJO类,pojo变量值可以是类全名,
* 代表一个确定的POJO,也可以是包名,代表该包下的所有POJO,
* 如“pojo = com.admin.user.User,com.admin.record;”代表com.admin.user.User
* 和com.admin.record包下的所有POJO类。
* 当bean模块执行完后,bean模块中定义的所有POJO会自动导入到pojo变量中来。
*
* db变量:DataBase,数据库,缺省为mysql,支持oracle、sqlite、sqlserver等主流数据库。
*
* ins变量:Include Super,是否包含POJO父类的属性,缺省为false,当ins为true时,数据表
* 和DAO方法会包括父类属性。
*
* anp变量:Annotation Prior,注释优先,缺省为false,当anp为true,并且POJO中存在Table、
* Column等Annotation时,表名和字段名将会优先使用注释中的声明。
*
* igf变量:Ignore Field,被忽略的属性,有些POJO的属性不会作为数据表的字段,igf的作用就
* 是忽略这些字段。
*
* fcm变量:Field Column Mapping,属性和数据表字段名的映射,缺省为*(*C_&name-u),即所
* 有类的所有属性对应的数据表字段名为“C_属性名大写”。
*
* ctm变量:Class Table Mapping,POJO和数据表名的映射,缺省为*T_&name-u,即所有的类的
* 数据表名为“T_类名大写”。
*
* osf变量:Object Source Folder,POJO的源文件夹,缺省为项目下的src目录。osf的值应当与
* bean模块中的src变量的值保持一致,bean模块中的src作用是告诉生成的POJO代码应当
* 放到哪里,而osf是告诉dao模块在哪里可以找到POJO的源文件。
*
* tsf变量:Generate To Source Folder,存放DAO,Proxy等源文件夹,缺省为项目下的src目录。
* 告诉dao模块将生成的DAO,Proxy等源代码放到哪里。
*
* gpx变量:Generate Proxy,是否生成DAO代理,缺省为true。
*
* gif变量:Generate Interface,是否生成DAO接口,缺省值为true,gif的值会参考gpx值,若gpx
* 变量为true,那么gif无论怎么设置都是为true,因为SPED DAO采用接口方式实现代理,
* 这是代理模式的最佳实践。
*
* owb变量:Object Write Back,是否可以将代码回写到POJO中,缺省为true,在没有特殊情况下,
* SPED强烈建议该值为true。
*
* cc变量:Connection Code,数据库连接(Connection)代码,table模块用该代码建立JDBC连接,
* 创建数据表,dao模块用该代码作为DAO的Connection。
*
* mod变量:Model,DAO方法的参数模式,缺省为2,mod = 1,DAO方法带有在执行完后是否关闭的
* Connection的询问参数close,mod = 2,DAO方法执行完将清理数据集并立即关闭
* Connection,mod = 3与mod = 2相似,只是DAO中没有setConnection方法,mod = 4,则
* 将Connection和close都作为DAO方法的参数。
*
*
*/
db = sqlite; // 使用嵌入式数据库SQLite
ctm = %user%:demo_user_&name-l, // 类全名带user字样的POJO数据表为“demo_user_类名小写”,下同
%resource%:demo_res_&name-l,
%auth%:demo_auth_&name-l,
%record%:demo_record_&name-l;
fcm = *(*c_&name-l); // 所有POJO的所有属性的字段名为“c_属性名小写”
cover = true; // 新的源代码是否覆盖旧的源代码,缺省为false
osf = demo;
tsf = demo;
cc = config.DemoUtil.getConnection();
igf = Resource:[method,actionClass];
/**
* table模块,数据库数据字典描述,用于创建POJO对应的数据表。
*
* table声明的主要方式为:
* 属性{ l:长度; c:字段约束; obj:POJO作用域;}
* l关键字:Length,属性对应字段长度,对需要进行长度设置的字段有效。
* c关键字:Constraint,字段约束,如c:PRIMARY KEY NOT NULL。
* obj关键字:POJO Object,POJO作用域,表示只针对哪些POJO,obj关键字也可以用于其他模块。
*
*/
table{
psql = true; // 是否打印创建数据表的SQL,缺省为true
drop = true; // 是否删除同名数据表,缺省为false
*{ l:100; } // 所有字段长度为100
id{ l:100; c:PRIMARY KEY; } // id的字段作为数据表的主键
fileName,url{ l:2000; } // fileName和url的字段长度为2000
}
/**
*
* dao模块,生成POJO相关的JDBC程序,包括DAO接口,DAO实现,DAO代理,DAO工厂等。
*
* DAO方法描述方式:
* 属性{ md:方法代号; }
*
*
* DAO方法声明用到的主要关键字:
* md关键字:Method,DAO方法代号,详情参见下面DAO方法代号。
*
* ct关键字:Combination Type,属性的组合方式,分别为s(single)和m(multiplex),m表
* 示DAO方法描述头部的属性形成组合参数生成一个DAO方法,
* 如[userName,password]{ md:f; ct:m; },
* 这个描述将会生成findComByUserNamePassword方法,常用于根据用户名和密码
* 进行登陆的DAO方法。
*
* obc关键字:Order By Config,描述查询DAO方法中SQL语句的order by。
* mt关键字:Match Type,SQL语句中字段与值的关系,缺省为“=”,mt关键字设置成“>”、
* “<”、“!=”、“like”或者它们的组合。
*
* rel关键字:Relationship,SQL语句where条件的字段与字段之间的关系,缺省为AND,rel关键字
* 可以设置成“OR”或者“AND”和“OR”的组合。
* 注意:SPED不推荐使用mt、rel、obc关键字,因为使用这些关键字会降低DAO方法的通用性。
*
*
* dao模块内部用到的主要变量:
* en变量:Enable,用于是否启用该模块,缺省为true,若为false则模块被忽略。
* dft变量:Dao Factory,Dao工厂类全名,若该类不存在,SPED根据dft变量自动创建该类,若存在,
* SPED将新的工厂方法代码追加到该类的源文件中。
*
* pkf变量:Primary Key Field,主键属性,指定POJO中的主键属性,pkf声明的属性用于POJO根据
* 主键的增、删、改、查,某些不能很好地支持分页查询数据库中,用pkf声明的变量做
* 分页查询,如SQL Server。
*
* DAO方法代号:
* f方法:Find,查询并返回单一POJO对象,一般用于通过主键或联合主键查询的DAO方法,
* 如public User findById(int id)。
*
* g方法:Get,查询并返回POJO的数组对象,如public UserAction[] getByUserId(int userId)。
*
* all方法:Get All,并返回POJO的所有对象,如public User[] getAll()。
* ga方法:Get By Array,通过数组作为参数,查询满足条件的所有对象,
* 如public User[] getById(int[] ids)。
*
* d方法:Delete,删除POJO对象数据,如public boolean deleteById(int id)。
*
* da方法:Delete By Array,以数组作为参数,删除满足条件的所有POJO数据,
* 如public boolean deleteById(int[] ids)。
*
* u方法:Update,以一个POJO对象作为参数,并根据该POJO对象的某些属性值作为条件进行更新,
* 如public boolean updateById(User user)。
*
* bu方法:Batch Update,批量更新,以POJO对象数组作为参数,根据POJO对象的某些属性值作为条件,
* 对数组中的每个POJO对象进行数据更新,如public boolean updateById(User[] users)。
*
* fc方法:Count By Field,用POJO某些属性值作为参数,查询返回满足条件的数据记录数,
* 如public int countByStatus(String status)。
*
* c方法:Count,获取POJO数据的总记录数,如public int count()。
*
* i方法:Insert,将一个POJO对象数据保存到数据库,如public boolean insert(User user)。
*
* bi方法:Batch Insert,批量保存,将POJO数组对象数据保存到数据库,
* 如public boolean insert(User[] users)。
*
* si方法:Step Insert,分步提交的方式批量保存POJO数组数据,适用于大数据量的保存,
* 如public boolean insert(User[] users, int stepCount)。
*
* ji方法:JSON Insert,将JSON保存到数据库,如public boolean insert(String userJSON)。
*
* jsi方法:JSON Step Insert,分步提交的方式批量保存JSON到数据库,
* 如public boolean insert(String userJSON, int stepCount)。
*
* e方法:Execute,执行没有数据集返回的SQL语句,如public boolean execute(String sql)。
*
* sql方法:Get Execute SQL,执行有数据集返回的SQL,如public User[] getSQL(String sql);
*
* m方法:Get To Map,执行SQL语句,若有数据集返回,将动态解析数据集,将解析到的数据存放到
* List<Map<String, Object>>对象中,该方法适用于任意的自定义SQL,
* 如public List<Map<String, Object>> getToMap(String sql)。
*
* fp方法:Paging By Field,根据某些属性进行分页查询,
* 如public User[] pagingByStatus(String status, int pageSize, int pageIndex)。
*
* p方法:Paging,分页查询POJO所有数据,public User[] paging(int pageSize, int pageIndex)。
*
*
*/
dao {
pkf = *id; // pkf变量,所有POJO的主键属性是id(没有id属性的POJO例外)。
dft = com.admin.factory.DAOFactory; // dft变量,声明工厂类
{ md: c,p,i,bi,m,e; } // 每个POJO都生成代号为c、p、i、bi、m、e对应的DAO方法
id{ md: ga,da,d,bu,u,f; } // 具有id属性的POJO都生成代号为ga、da、d、bu、u、f对应的DAO方法
%Id{ md: g,ga,fp,fc; } // 具有属性名以Id结尾的属性的POJO都生成代号为g、ga、fp、fc对应的DAO方法
status{ md: g; } // 具有status属性的POJO都生成代号为g对应的DAO方法
}