sharp-common如果优雅地异常处理

定义异常枚举类,实现接口Assert

@Getter
@ToString
public enum ExceptionCodeEnum implements Assert {
DIMENSION_CODE_DUPLICATE(50100, "维度code、单位code不能重复,全局唯一"),
UNIT_CODE_DUPLICATE(50101, "单位code不能重复"),
LESS_BASE_UNIT_ERROR(50102, "缺少一个基准单位%s"),
MAX_TRY_LOGIN_ERROR(50103, "MAX_TRY_LOGIN_ERROR"), // messages.properties: MAX_TRY_LOGIN_ERROR=该账号{0}次登录失败后,被锁定{1}分钟

private int code;
private String msg;

ExceptionCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

使用异常

  • 数据库中不存在某个对象,抛出异常“库房不存在“
Assert.notExists("库房");
  • 参数不能为空,抛出异常“库房id不能为空“
Assert.notNull(id, "库房id");
  • 业务异常
ExceptionCodeEnum.DIMENSION_CODE_DUPLICATE.exception();
  • 参数格式化错误信息
throw new BizException(ExceptionCodeEnum.LESS_BASE_UNIT_ERROR.result(), new Object[] {"米"});

异常信息为“缺少一个基准单位米”

  • 参数国际化错误信息
throw new BizException(ExceptionCodeEnum.MAX_TRY_LOGIN_ERROR.result(), new Object[] {"0001", "10"});

异常信息为“该账号001次登录失败后,被锁定10分钟”

sharp-database中的BaseDAOImpl实现多表级联@ManyToMany(三)

级联查询,在中间表中建立关系。不会级联更新!!!

创建实体对象

Usre.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Table("t_user")
public class User extends BasePureEntity {

    private String name;

    @ManyToMany(thirdPartyTable = "t_user_role", referenceColumnName = "role_id", columnDefinition = "user_id", referenceTable = "t_role")
    private List<Role> roleList;

}

Role.java

/**
 * @author Rick
 * @createdAt 2022-03-03 19:26:00
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Table("t_role")
public class Role extends BasePureEntity {

    private String name;

}

生成3张表 t_user t_role t_user_role。中间表t_user_role 只有三个字段 user_id role_id is_deleted 级联删除的时候需要该字段。

添加测试

    @Test
    public void testInsert() {
        User user = User.builder()
                .name("Rick")
                .roleList(Arrays.asList(Role.builder().id(530861443353075712L).build()))
                .build();

        int count = personDAO.insert(user);
        assertThat(count).isEqualTo(1);

        Optional<User> personOptional = personDAO.selectById(user.getId());
        User person2 = personOptional.get();
        assertThat(person2.getRoleList().get(0).getName()).isEqualTo("admin");
    }

    @Test
    public void testUpdate() {
        User user = User.builder()
                .id(530865367078330368L)
                .name("Tom")
                .roleList(Arrays.asList(Role.builder().id(530861443353075712L).build()
//                        , Role.builder().id(530861443353075713L).build()
                        , Role.builder().id(530861443353075714L).build())
                )
                .build();

        personDAO.update(user);
    }

    @Test
    public void testSelect() {
        Optional<User> personOptional = personDAO.selectById(530864959987572736L);
        User user = personOptional.get();
        assertThat(user.getRoleList().get(0).getName()).isEqualTo("admin");
    }

    @Test
    public void testDelete() {
        personDAO.deleteById(530865367078330368L);
    }

sharp-database中的TableGenerator根据实体对象生成表

1. 创建实体对象

Task.java

@SuperBuilder
@Getter
@Setter
@Table(value = "t_task", comment = "任务表")
@NoArgsConstructor
public class Task extends BasePureEntity {

    @Column(nullable = true, comment = "任务名称")
    private String taskName;

    @Column(comment = "完成时间")
    private LocalDateTime completeTime;

    private Integer costHours;

    private Boolean complete;

    private Long assignUserId;

    @Column(comment = "用户状态")
    private UserStatusEnum userStatus;

}

2. 执行代码

@SpringBootTest
public class TableGeneratorTest {

    @Autowired
    private TableGenerator tableGenerator;

    @Test
    public void createTable() {
        tableGenerator.createTable(Task.class);
    }

}

生成sql,并执行

create table t_task
(
    id bigint not null comment '主键'
        primary key,
    task_name varchar(32) not null comment '任务名称',
    complete_time datetime null comment '完成时间',
    cost_hours int null,
    complete bit null,
    assign_user_id bigint null,
    user_status varchar(16) null comment '用户状态',
    created_by bigint null,
    created_at datetime null,
    updated_by bigint null,
    updated_at datetime null,
    is_deleted bit null
)
comment '任务表';

sharp-formflow中自定义表单

自定义表单

自定义表单顾名思义就是由用户定义表单,由用户觉定标签和组件,决定要填写什么格式的数据。

创建表单

方式一

创建组件

http://localhost:8080/form/configurers

[{
    "label": "姓名",
    "cpnType": "TEXT",
    "validators": [{
        "min": 0,
        "max": 16,
        "message": "长度范围 0 - 16 个字符",
        "validatorType": "LENGTH"
    }, {
        "required": true,
        "message": "必填项",
        "validatorType": "REQUIRED"
    }],
    "defaultValue": "Rick",
    "placeholder": "请输入姓名"
}, {
    "label": "年龄",
    "cpnType": "NUMBER_TEXT",
    "validators": [{
        "min": 18,
        "max": 100,
        "message": "大小范围是18 - 100",
        "validatorType": "SIZE"
    }, {
        "required": true,
        "message": "必填项",
        "validatorType": "REQUIRED"
    }],
    "defaultValue": "18",
    "placeholder": "请输入年龄"
}, {
    "label": "兴趣爱好(单选)",
    "cpnType": "SELECT",
    "validators": [{
        "required": true,
        "message": "必填项",
        "validatorType": "REQUIRED"
    }],
    "options": ["足球", "篮球", "乒乓球", "羽毛球"],
    "defaultValue": "足球",
    "placeholder": "请输入兴趣爱好"
}, {
    "label": "信息收集",
    "cpnType": "TABLE",
    "validators": [],
    "placeholder": "请输入信息收集",
    "additionalInfo": {
        "labels": ["姓名", "年龄"]
    }
}, {
    "label": "兴趣爱好(多选)",
    "cpnType": "CHECKBOX",
    "validators": [],
    "options": ["足球", "篮球", "乒乓球", "羽毛球"],
    "defaultValue": "[\"足球\", \"篮球\"]",
    "placeholder": "请输入兴趣爱好"
}]

创建表单

http://localhost:8080/form/configurers

{
    "name": "人员信息登记表"
}

表单关联组件

http://localhost:8080/forms/488683635382583296/configs

["488675379486556160","488675379490750464","488675379490750465","488675379490750466","488675379494944768"]

方式二

创建表单和组件并关联

{
    "form": {
        "name": "我的第N个表单"
    },
    "configs": [{
        "label": "姓名",
        "cpnType": "TEXT",
        "validators": [{
            "min": 0,
            "max": 16,
            "message": "长度范围 0 - 16 个字符",
            "validatorType": "LENGTH"
        }, {
            "required": true,
            "message": "必填项",
            "validatorType": "REQUIRED"
        }],
        "defaultValue": "Rick",
        "placeholder": "请输入姓名"
    }, {
        "label": "年龄",
        "cpnType": "NUMBER_TEXT",
        "validators": [{
            "min": 18,
            "max": 100,
            "message": "大小范围是18 - 100",
            "validatorType": "SIZE"
        }, {
            "required": true,
            "message": "必填项",
            "validatorType": "REQUIRED"
        }],
        "defaultValue": "18",
        "placeholder": "请输入年龄"
    }, {
        "label": "兴趣爱好(单选)",
        "cpnType": "SELECT",
        "validators": [{
            "required": true,
            "message": "必填项",
            "validatorType": "REQUIRED"
        }],
        "options": ["足球", "篮球", "乒乓球", "羽毛球"],
        "defaultValue": "足球",
        "placeholder": "请输入兴趣爱好"
    }, {
        "label": "信息收集",
        "cpnType": "TABLE",
        "validators": [],
        "placeholder": "请输入信息收集",
        "additionalInfo": {
            "labels": ["姓名", "年龄"]
        }
    }, {
        "label": "兴趣爱好(多选)",
        "cpnType": "CHECKBOX",
        "validators": [],
        "options": ["足球", "篮球", "乒乓球", "羽毛球"],
        "defaultValue": "[\"足球\", \"篮球\"]",
        "placeholder": "请输入兴趣爱好"
    }]
}

查看表单

http://localhost:8080/forms/ajax/488686638231617536

{
    "form": {
        "id": "488686638231617536",
        "name": "我的第N个表单"
    },
    "instanceId": null,
    "propertyList": [
        {
            "id": 488686640483958784,
            "name": "ZghRwhvcGu",
            "configurer": {
                "id": "488686639036923904",
                "label": "姓名",
                "cpnType": "TEXT",
                "validators": [
                    {
                        "min": 0,
                        "max": 16,
                        "message": "长度范围 0 - 16 个字符",
                        "validatorType": "LENGTH"
                    },
                    {
                        "required": true,
                        "message": "必填项",
                        "validatorType": "REQUIRED"
                    }
                ],
                "options": null,
                "defaultValue": "Rick",
                "placeholder": "请输入姓名",
                "additionalInfo": null
            },
            "value": "Rick",
            "validatorProperies": {
                "Required.required": true,
                "Length.min": 0,
                "Length.max": 16
            }
        },
        {
            "id": 488686640483958785,
            "name": "VnmnWkziwe",
            "configurer": {
                "id": "488686639036923905",
                "label": "年龄",
                "cpnType": "NUMBER_TEXT",
                "validators": [
                    {
                        "min": 18,
                        "max": 100,
                        "message": "大小范围是18 - 100",
                        "validatorType": "SIZE"
                    },
                    {
                        "required": true,
                        "message": "必填项",
                        "validatorType": "REQUIRED"
                    }
                ],
                "options": null,
                "defaultValue": "18",
                "placeholder": "请输入年龄",
                "additionalInfo": null
            },
            "value": 18,
            "validatorProperies": {
                "Size.min": 18,
                "Size.max": 100,
                "Required.required": true
            }
        },
        {
            "id": 488686640488153088,
            "name": "WIRyhGtAhh",
            "configurer": {
                "id": "488686639036923906",
                "label": "兴趣爱好(单选)",
                "cpnType": "SELECT",
                "validators": [
                    {
                        "required": true,
                        "message": "必填项",
                        "validatorType": "REQUIRED"
                    }
                ],
                "options": [
                    "足球",
                    "篮球",
                    "乒乓球",
                    "羽毛球"
                ],
                "defaultValue": "足球",
                "placeholder": "请输入兴趣爱好",
                "additionalInfo": null
            },
            "value": "足球",
            "validatorProperies": {
                "Required.required": true
            }
        },
        {
            "id": 488686640488153089,
            "name": "EsaBSGeoNF",
            "configurer": {
                "id": "488686639041118208",
                "label": "信息收集",
                "cpnType": "TABLE",
                "validators": [],
                "options": null,
                "defaultValue": null,
                "placeholder": "请输入信息收集",
                "additionalInfo": {
                    "labels": [
                        "姓名",
                        "年龄"
                    ]
                }
            },
            "value": null,
            "validatorProperies": {}
        },
        {
            "id": 488686640488153090,
            "name": "dCrOsUvnkb",
            "configurer": {
                "id": "488686639041118209",
                "label": "兴趣爱好(多选)",
                "cpnType": "CHECKBOX",
                "validators": [],
                "options": [
                    "足球",
                    "篮球",
                    "乒乓球",
                    "羽毛球"
                ],
                "defaultValue": "[\"足球\", \"篮球\"]",
                "placeholder": "请输入兴趣爱好",
                "additionalInfo": null
            },
            "value": [
                "足球",
                "篮球"
            ],
            "validatorProperies": {}
        }
    ],
    "method": "POST",
    "actionUrl": "488686638231617536"
}

填写表单数据

我的模版是是基于 thymeleaf jQuery Bootstrap 实现的。
浏览器访问:http://localhost:8080/forms/page/488686638231617536

http://xhope.top/wp-content/uploads/2021/11/1122.png
数据填写完成后,会生实例id

实例id查看实例数据

488693516797902848 就是实例id

http://localhost:8080/forms/page/488686638231617536/488693516797902848

sharp-database中的BaseDAOImpl实现多表级联@OneToMany(二)

目标

本文章主要介绍级联中的「插入」「查询」「删除」操作。

环境搭建

  • Project.java
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@TableName("p_project")
public class Project extends BaseComponentEntity {

    private String title;

    private String description;

    private Long ownerId;

    private String coverUrl;

    @ManyToOne(value = "project_group_id", parentTable = "p_project_group")
    private ProjectGroup projectGroup;
}

@ManyToOne 是子表注解,它有2个属性,value 表示引用外键的字段名,parentTable 引用的父表名。

  • ProjectGroup.java
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@TableName("p_project_group")
public class ProjectGroup extends BaseComponentEntity {

    private String title;

    @OneToMany(subTable = "p_project", reversePropertyName = "projectGroup", cascadeSaveOrUpdate = true)
    private List<Project> projectList;

}

@OneToMany 是父表注解,属性是 List 集合。它有2个属性,reversePropertyName 表示子对象中该对象的属性名,subTable 关联的子表名。cascadeSaveOrUpdate 开启级联更新。

测试

级联插入

@Test
public void save() {
    ProjectGroup projectGroup = ProjectGroup.builder().title("我的组").projectList(Lists.newArrayList(Project.builder().title("我的项目").ownerId(11L).build())).build();
    projectGroupService.save(projectGroup);

    Project project = Project.builder().title("我的项目-2").ownerId(11L).build();
    project.setProjectGroup(projectGroup);
    projectService.save(project);
}

projectGroupService.save 可以级联插入;projectService.save单个插入,但是属性需要关联对象 ProjectGroup,需要对象中的 id 信息。

级联查询

@Test
public void select() {
    ProjectGroup projectGroup = projectGroupService.getByTitle("我的组").get(0);
    Assert.assertEquals("我的项目", projectGroup.getProjectList().get(0).getTitle());
    Assert.assertEquals("我的项目-2", projectGroup.getProjectList().get(1).getTitle());

    Project project = projectService.findById(486880981274755072L).get();
    Assert.assertEquals("我的组", project.getProjectGroup().getTitle());
}

级联删除

@Test
public void deleteLogically() {
    projectGroupService.deleteLogically(486678250836623360L);
    Assert.assertEquals(false, projectService.findById(486678250924703744L).isPresent());
}

@Test
public void deleteHardly() {
    projectGroupService.delete(486678250836623360L);
}