# efeichong-common **Repository Path**: lixuankun/efeichong-common ## Basic Information - **Project Name**: efeichong-common - **Description**: 基础包 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-11-13 - **Last Updated**: 2024-09-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 提供数据库操作,excel操作,同步锁,代码生成器,webSocket,oss和常用util工具 maven引用方式 ```xslt com.efeichong (不同模块参考下文) 1.0.9 ``` # 基础框架工具包 ## jpa-common: ``` 基于jpa封装的便捷增删改查的工具,使单表联表查询更简单,也提供了比hibernate批量操作更优的批量处理 ``` >本模块是基于jpa封装的便捷增删改查的工具,使用要求: - 建表要规范,严格使用外键约束 - 数据交互使用vo,不得直接使用po - 有jpa的基础使用概念,如:一对多关系,一对一关系,多对一关系,多对多关系,懒加载,及时加载 - 除直接使用sql外,一切增删改查都是基于对象操作,数据库表即是对象,数据库字段即是对象属性 - 自定义dao要继承与BaseDao ### 功能 * 增删改的操作 * 提供Example进行单表,连表,排序,分页,悲观锁,条件查询的操作 * 逻辑删除,字段值0(未删除),uuid生成的值(已删除),如果有逻辑删除的表有唯一约束请将唯一约束的条件加上逻辑删除字段 * 多租户 在MultiTenantIdHolder中设置租户 * 使用@Mapping自动映射将po映射到vo进行返回 * 分页查询 如果没设定分页参数默认只返回10条 ### 新增/更新 ```xslt /** * 新增/更新 id不在则新增,存在则更新(默认所有字段不能更改为空) * * @param entity * @param * @param config 配置忽略更新的字段和可以更新为空的字段 {@link UpdateGlobalConfig#ALL 包含所有字段 UpdateGlobalConfig#NONE 不好含任何字段} * @return */ S save(@NonNull S entity, UpdateGlobalConfig... config); /** * 批量新增/更新 id不在则新增,存在则更新(默认所有字段不能更改为空) * * @param entities * @param config 配置忽略更新的字段和可以更新为空的字段 {@link UpdateGlobalConfig#ALL 包含所有字段 UpdateGlobalConfig#NONE 不好含任何字段 默认 DefaultUpdateGlobalConfig} * @return */ List saveAll(@NonNull Collection entities, UpdateGlobalConfig... config); /** * 根据条件进行更新 id不在则新增,存在则更新(默认所有字段不能更改为空) * * @param entity * @param example * @param * @param config 配置忽略更新的字段和可以更新为空的字段 {@link UpdateGlobalConfig#ALL 包含所有字段 UpdateGlobalConfig#NONE 不好含任何字段} * @return */ List saveByExample(@NonNull S entity, Example example, UpdateGlobalConfig... config); ``` ### 删除 ```xslt /** * 删除 (当实体类有hasDel字段时触发逻辑删除) * * @param entity */ void delete(@NonNull T entity); /** * 通过id删除(当实体类有hasDel字段时触发逻辑删除) * * @param id */ void deleteById(@NonNull ID id); /** * 删除全部(当实体类有hasDel字段时触发逻辑删除) */ void deleteAll(); /** * 批量删除(当实体类有hasDel字段时触发逻辑删除) * * @param entities 对象集合 */ void deleteAll(@NonNull Iterable entities); /** * 根据id批量删除(当实体类有hasDel字段时触发逻辑删除) * * @param ids id集合 * @return */ void deleteAllByIds(@NonNull Iterable ids); /** * 根据条件删除(当实体类有hasDel字段时触发逻辑删除) * * @param example */ void deleteAll(@NonNull Example example); ``` ### 批量操作 ```xslt /** * 批量新增 直接执行sql * 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 * 它不会级联到其他实体 * 不会操作持久性上下文 * * @param entities */ void batchSave(@NonNull Collection entities); /** * 批量更新 直接执行sql * 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 * 它不会级联到其他实体 * 不会操作持久性上下文 * * @param entities */ void batchUpdate(@NonNull Collection entities); /** * 批量删除 直接执行sql * 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 * 它不会级联到其他实体 * 您必须清除持久性上下文或假设它已失效 */ void batchDelete(); /** * 批量删除 直接执行sql * 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 * 它不会级联到其他实体 * 您必须清除持久性上下文或假设它已失效 * * @param entities */ void batchDelete(@NonNull Collection entities); /** * 批量删除 直接执行sql * 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 * 它不会级联到其他实体 * 您必须清除持久性上下文或假设它已失效 * * @param idCollection */ void batchDeleteByIds(@NonNull Collection idCollection); ``` ### 查询 ```xslt /** * 查询全表 * * @return 如果没有结果将返回空集合并非null */ List selectAll(); /** * 查询全表返回Vo * * @param voClazz 返回VO * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return */ List selectAll(Class voClazz, String... ignoreProperties); /** * 查询结果只有一条 * * @param example 查询条件 无法使用分页 * @return 如果不存在将会返回{@link Optional#empty()} * @throws javax.persistence.NonUniqueResultException 如果返回对象数超过一个将会抛这个异常 */ Optional selectOne(@NonNull Example example); /** * 查询结果只有一条 * * @param example 查询条件 无法使用分页 * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return 如果不存在将会返回{@link Optional#empty()} * @throws javax.persistence.NonUniqueResultException 如果返回对象数超过一个将会抛这个异常 */ Optional selectOne(@NonNull Example example, Class voClazz, String... ignoreProperties); /** * 条件查询 * * @param example 查询条件 开启分页后仍返回list对象 * @return 如果没有结果将返回空集合并非null */ List selectAll(@Nullable Example example); /** * 条件查询返回Vo * * @param example 查询条件 开启分页后仍返回list对象 * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return */ List selectAll(@Nullable Example example, Class voClazz, String... ignoreProperties); /** * 通过主键查询 * * @param id 主键 * @return 如果不存在将会返回{@link Optional#empty()} */ Optional selectById(@NonNull ID id); /** * 通过主键查询返回vo * * @param id * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @param * @return */ Optional selectById(@NonNull ID id, Class voClazz, String... ignoreProperties); /** * 通过id查询列表 * * @param ids * @return */ List selectByIds(@NonNull Collection ids); /** * 通过id查询列表返回Vo * * @param ids * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return */ List selectByIds(@NonNull Collection ids, Class voClazz, String... ignoreProperties); /** * 根据条件查询库中的行数 * * @param example 查询条件 * @return 行数 */ long count(@Nullable Example example); /** * 是否存在满足该条件的记录 * * @param example 查询条件 * @return false不存在 true存在 */ boolean exist(@Nullable Example example); /** * 是否存在该id * * @param id 主键 * @return false不存在 true存在 */ boolean existsById(@NonNull ID id); /** * 分页查询,默认使用前端传的参数 @params{pageIndex,pageSize},如果前端没有传这个参数 * 也可以通过{@link Example#startPage(int, int)}开启,如果同时存在优先使用手动开启的 * 不指定则默认 pageIndex=1 pageSize=10 * * @param example 查询条件 * @return 返回分页对象 */ PageData selectByPage(@NonNull Example example); /** * 分页查询,默认使用前端传的参数 @params{pageIndex,pageSize},如果前端没有传这个参数 * 也可以通过{@link Example#startPage(int, int)}开启,如果同时存在优先使用手动开启的 * 不指定则默认 pageIndex=1 pageSize=10 * * @param example 查询条件 * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return 返回分页对象 */ PageData selectByPage(@NonNull Example example, Class voClazz, String... ignoreProperties); /** * 分页查询,默认使用前端传的参数 @params{pageIndex,pageSize},如果前端没有传这个参数 * 也可以通过{@link Example#startPage(int, int)}开启,如果同时存在优先使用手动开启的 * 不指定则默认 pageIndex=1 pageSize=10 * * @return 返回分页对象 */ PageData selectByPage(); /** * 分页查询,默认使用前端传的参数 @params{pageIndex,pageSize},如果前端没有传这个参数 * 也可以通过{@link Example#startPage(int, int)}开启,如果同时存在优先使用手动开启的 * 不指定则默认 pageIndex=1 pageSize=10 * * @param voClazz 返回的vo 里面的字段可以通过{@link com.efeichong.mapping.Mapping} 进行映射 * @param ignoreProperties 不需要查到vo的字段 * @return 返回分页对象 */ PageData selectByPage(Class voClazz, String... ignoreProperties); /** * native查询 * * @param sql 查询sql * @param resultClazz 返回的对象 class * @param params 查询参数 * @return */ List selectBySql(@NonNull String sql, @NonNull Class resultClazz, @NonNull Map params); /** * native查询 * * @param sql 查询sql * @param resultClazz 返回的对象 class * @return */ List selectBySql(@NonNull String sql, @NonNull Class resultClazz); /** * native查询 * * @param sql 查询sql * @param resultClazz 返回的对象 class * @param params 查询参数 * @return */ V selectOneBySql(@NonNull String sql, @NonNull Class resultClazz, @NonNull Map params); /** * native查询 * * @param sql 查询sql * @param resultClazz 返回的对象 class * @return */ V selectOneBySql(@NonNull String sql, @NonNull Class resultClazz); ``` ### 逻辑删除 在bean类上面加@LogicDelete(column = "your column")即可实现逻辑删除 1. 字段是驼峰的 2. 字段类型必须是long/Long或者String类型 3. 字段值0(未删除),uuid生成的值(已删除) 4. 如果逻辑删除的表有唯一约束请将唯一约束的条件拼上逻辑删除字段 ### 多租户 在bean类上面加@EnableMultiTenant(column = "your column")实现多租户 1. 字段是驼峰的 2. 根据该字段实现多租户,查询时会将租户id为空的或租户id相等的结果筛选出来 ###Example ```xslt 快捷创建eqaul条件查询 Example.of() 例如 Example.of(id,1) Example.of(id,1,name,2) Example.of(school.id) Example example = new Example(); //排序 example.orderBy().asc("id"); example.orderBy().asc("school.id"); //分组 example.groupBy("id"); example.groupBy("school.id"); //分页 example.startPage(1,10); //关闭分页 example.stopPage(); //查询不携带逻辑删除的条件 example.ignoreFakeDel(); //查询不区分多租户 example.ignoreMultiTenant(); //去重 example.setDistinct(true) //将传入对象拼接成条件 //以*开头条件为 like "%keyword" 以*结尾条件为 like "keyword%" //集合或数组类型条件为 in //字段名以begin/end开头 条件为between between(beginCreateTime,endCreateTime) //sortOptions字段会进行排序 sortOptions{"id":"desc","sort":"asc"} //使用@Mapping会进行连表left join查询 例如 @Mapping(poProperty="school.id",useModel=UseMode.QUERY) //默认使用equals example.initParam(schoolVo) //创建一个条件 Criteria criteria = example.createCriteria() 例如 /** * @param removeEmptyValueCondition 默认为false 为true时 value为空时条件不生效 in集合为空条件不生效 between任意一个为空条件不生效 * 为false时 value为空会报错 in集合为空报错 between任意一个为空报错 * @parmm value 为字段时使用 {@link Column#of(String)} */ public Criteria andEqualTo(String property, Object value, boolean... removeEmptyValueCondition) { addAndCriteria(ConditionType.EQ, property, value, removeEmptyValueCondition); return this; } criteria.andEqualTo("school.name","11"); //查询学校名称和学生名称相等的 criteria.andEqualTo("school.name",Column.of("user.name")); //拼接条件 年龄>1 criteria. andExpression(new ExecExpression() { @Override public Predicate genExpression(Root root, CriteriaBuilder criteriaBuilder) { return criteriaBuilder.gt(root.get("age"),criteriaBuilder.literal(1)); } }); //这个条件会自动去掉 criteria.andEqualTo("school.name",null,true); //这个会提示value不能为空 criteria.andEqualTo("school.name",null); //一个括号 example.and() and () example.or() or () //悲观锁 example.setForUpdate(true) select * from school where id = 1 for update ``` ### PO和VO的映射 使用@Mapping注解实现映射功能,此注解加在vo层的字段上 ```xslt @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Mapping { /** * 映射po的字段 * * @return */ String poProperty(); /** * 指定在转po或者转vo时生效,默认全部生效 * UseMode.ALL 都生效 * UseMode.TO_PO vo转po时生效 vo指定的id会被转为po对应的对象 * UseMode.TO_VO po转vo时生效 * UseMode.QUERY 查询时生效 * * @return */ UseMode[] useMode() default UseMode.ALL; } ``` ### PO和VO的转换 ```xslt //po转vo TransformUtils.toVo //vo转po TransformUtils.toPo //Collection类型转换为List 将po集合转为vo集合 TransformUtils.toVos //Collection类型转换为List 将vo集合转为po集合 TransformUtils.toPos ``` ### 锁 - 乐观锁 在指定版本号的字段上加@Version ```xslt @Version private Long version; ``` - 悲观锁 example.setForUpdate(true); ### 样例 bean1 ```xslt @Setter @Getter @Entity @Table(indexes = { @Index(name = "PRIMARY", columnList = "id", unique = true), }) @DynamicInsert @DynamicUpdate @LogicDelete(column = "hasDel") public class School { @Column(name = "[id]", columnDefinition = "bigint ") @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /**学校名**/ @Column(name = "[name]", columnDefinition = "varchar (255) comment '学校名'") private String name; /**创建时间**/ @Column(name = "[create_time]", columnDefinition = "datetime comment '创建时间'") private Date createTime; /**更新时间**/ @Column(name = "[update_time]", columnDefinition = "datetime comment '更新时间'") private Date updateTime; /**版本号**/ @Column(name = "[version]", columnDefinition = "bigint comment '版本号'") private Long version; /**租户id**/ @Column(name = "[tenant_id]", columnDefinition = "varchar (20) comment '租户id'") private String tenantId; /**是否删除0未删除**/ @Column(name = "[has_del]", columnDefinition = "bigint comment '是否删除0未删除'") private Long hasDel; @OneToMany(mappedBy = "school",fetch = FetchType.LAZY) private List students; } ``` bean2 ```xslt @Setter @Getter @Entity @Table(indexes = { @Index(name = "PRIMARY", columnList = "id", unique = true), @Index(name = "fk_school_student_id", columnList = "school_id"), }) @DynamicInsert @DynamicUpdate public class Student { @Column(name = "[id]", columnDefinition = "bigint ") @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /**学生姓名**/ @Column(name = "[name]", columnDefinition = "varchar (255) comment '学生姓名'") private String name; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "[school_id]") private School school; } ``` dao1 ```$text public interface SchoolDao extends BaseDao { } ``` dao2 ```$text public interface StudentDao extends BaseDao { } ``` vo1 ```$text @Setter @Getter @ApiModel("学校") public class SchoolVo { @ApiModelProperty(value = "") private Long id; @ApiModelProperty(value = "学校名") private String name; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "版本号") private Long version; @ApiModelProperty(value = "租户id") private String tenantId; @Mapping(poProperty = "students.name",useMode = UseMode.TO_VO) @ApiModelProperty(value = "学校中所有的学生名") private List studentNames; @Mapping(poProperty = "students",useMode = UseMode.TO_VO) @ApiModelProperty(value = "学校中所有的学生") private List studentVos; } ``` vo2 ```$text @Setter @Getter @ApiModel("学生") public class StudentVo { private Long id; @ApiModelProperty(value = "学生姓名") private String name; @Mapping(poClazz = Student.class,poProperty = "school.name",useMode = UseMode.TO_VO) @ApiModelProperty(value = "学校名") private String schoolName; //tovo时 student中的school会转成schoolId topo时schoolId会转成student中的school对象 @Mapping(poClazz = Student.class,poProperty = "school.id",useMode = UseMode.ALL) private Long schoolId; } ``` 使用样例代码 ```$xslt Example example = new Example<>(); //查询北大的学生 example.createCriteria().andLike("school.name","北大",LikeType.LEFT_AND_RIGHT); sql = "select s.* from student s left join school sc on sc.id = s.school.name where sc.id like '%北大%'" //查询school id=1的学生,schoolId为空自动移除该条件 example.createCriteria().andEqualTo("school.id",schoolId,true); //查询学校创建时间=更新时间的学生 example.createCriteria().andEqualTo("school.createTime",Column.of("school.updateTime")); sql = "select s.* from student s left join school sc on sc.id = s.school.name where sc.create_time=sc.update_time" List students = studentDao.selectAll(example); //分页查询所有学生 example.startPage(1,20); PageData page = studentDao.selectByPage(example); sql = "select s.* from student s limit 0,20 " //查询学校名称和学生名称相等的 criteria.andEqualTo("school.name",Column.of("user.name")); //拼接条件 年龄>1 criteria. andExpression(new ExecExpression() { @Override public Predicate genExpression(Root root, CriteriaBuilder criteriaBuilder) { return criteriaBuilder.gt(root.get("age"),criteriaBuilder.literal(1)); } }); //将vo初始化成条件 StudentVo vo = new StudentVo(); vo.setSchoolId(1); vo.setName("11*"); example.initParam(vo); sql = "select s.* from student s left join school sc on sc.id = s.school.name where sc.id like '11%' and sc.id=1" //直接查询成vo List studentVos = studentDao.selectAll(example, StudentVo.class); sql = "select s.id,s.name,sc.name as schoolName,sc.id as schoolId from student s left join school sc on sc.id = s.school.name" //主键查询 Optional optional = studentDao.selectById(1L); //删除指定学生 Student student = optional.get(); studentDao.delete(student); //通过id删除 studentDao.deleteById(1L); //根据条件删除 studentDao.deleteAll(example); //根据id批量删除 studentDao.deleteAllByIds(Lists.newArrayList(1L)); StudentVo studentVo = new StudentVo(); studentVo.setName("王五"); studentVo.setSchoolId(1L); Student save = studentDao.save(TransformUtils.toPo(Student.class, studentVo)); save.setName("王五"); //有id则更新,无id则新增 studentDao.save(save); studentDao.save(save, new UpdateGlobalConfig() { @Override public String[] ignoreUpdateProps() { //忽略更新的字段 return new String[0]; } @Override public String[] canBeNullProps() { //可以更新为空的字段 return new String[]{"name"}; } }); //native查询 List list = studentDao.selectBySql("select * from student where id = 1", Student.class); //批量新增 这不会调用您可能拥有的任何 JPA/Hibernate 生命周期钩子 它不会级联到其他实体 不会操作持久性上下文 studentDao.batchSave(list); ``` # 代码生成器 #### 生成po,dao,vo,service,serviceImpl,controller,vue文件 1. 一对多/多对多等关联关系的生成需要在数据库表中指定外键关联 2. 一对一关系需指定两张表的外键关联且关联字段有唯一约束 3. 对多对中间表定义:只有两个字段且都有外键约束 #### bean类 | 参数 | 值 | 说明 | 是否必须 | | --- |--- | --- | --- | | genConfiguration |GenConfiguration | 配置 | 否| | requireTableNames |set | 指定哪些表需要进行代码生成 |否| | ignoreTableNames |set | 不需要生成的表 |否| | hasExcel |false | 是否生成excel相关代码 |否| | manyToManyMappedTableMap |Map | 指定多对多映射的表维护的一方 key:多对多中间表 value:维护者 表名字 |否| | dataSource |DataSource | 数据库连接配置 |是| | generateModel |int | 指定生成的文件类型,默认生成全部 |否| #####generateModel: ```$xslt 指定生成的文件类型 默认: DOMAIN_TYPE | DAO_TYPE | SERVICE_TYPE | SERVICE_IMPL_TYPE | CONTROLLER_TYPE | VO_TYPE | VUE_TYPE; 生成多个以 | 进行连接 例如: DOMAIN_TYPE|SERVICE_TYPE|CONTROLLER_TYPE /* 实体类 */ public static final int DOMAIN_TYPE = 2; /* dao持久层 */ public static final int DAO_TYPE = 2 << 1; /* service接口 */ public static final int SERVICE_TYPE = 2 << 2; /* serviceImpl实现类 */ public static final int SERVICE_IMPL_TYPE = 2 << 3; /* controller对外接口 */ public static final int CONTROLLER_TYPE = 2 << 4; /* vo层 */ public static final int VO_TYPE = 2 << 5; /** 生成vue代码 */ public static final int VUE_TYPE = 2 << 6; `` #### example: ```$xslt //指定要生成代码的表 Set tables = Sets.newLinkedHashSet(); tables.add("sys_user"); tables.add("sys_dept"); tables.add("sys_post"); tables.add("sys_role"); tables.add("sys_menu"); tables.add("sys_dict"); tables.add("sys_dict_detail"); tables.add("sys_base_org"); //指定数据库配置 DataSource dataSource = new DataSource(); dataSource.setDriver("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:9090/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); //自定义配置 GenConfiguration configuration = new GenConfiguration(); //要继承的类 //configuration.setBaseEntity(BaseEntity.class); //po包路径 configuration.setDomainPackage("com.efeichong.project.domain.entity.db"); //dao包路径 configuration.setDaoPackage("com.efeichong.project.domain.repository.db"); //service包路径 configuration.setServicePackage("com.efeichong.project.service"); //service实现类包路径 configuration.setServiceImplPackage("com.efeichong.project.service.impl"); //controller包路径 configuration.setControllerPackage("com.efeichong.project.controller"); //vue页面 configuration.setVuePackage("com.efeichong.project.vue"); //指定关系的维护方 Map map = ImmutableMap.of("sys_user_post_ship","sys_user","sys_user_dept_ship","sys_user"); Generator generator = Generator.builder().addGenConfiguration(configuration) .addRequireTableNames(tables) //是否生成excel相关代码 .addHasExcel(true) .addDataSource(dataSource) .addManyToManyMappedTableMap(map) //默认全部 DOMAIN_TYPE|DAO_TYPE | SERVICE_TYPE | SERVICE_IMPL_TYPE | CONTROLLER_TYPE | VO_TYPE | VUE_TYPE .addGenerateModel(GenConstant.VO_TYPE|GenConstant.DAO_TYPE) .build(); generator.generate(); ``` # Excel导入导出 ##基于easyExcel增加了一点功能,详细使用请查看 https://github.com/alibaba/easyexcel ### 导出: #### bean类 ```$xslt @Setter @Getter @Entity @ExcelPropertyIgnoreUnannotated @ContentRowHeight(60) @ContentFontStyle(italic = BooleanEnum.FALSE,fontName = "Arial") @HeadRowHeight(40) @ContentStyle(locked=BooleanEnum.TRUE,wrapped=BooleanEnum.TRUE) public class Test { @ExcelProperty(value = "用户名",order = 10) private String username; @ExcelProperty(value = "年龄",converter = TestConvert.class) private Integer age; @ExcelDict({"0=男", "1=女"}) @ExcelProperty(value = "性别",convert = ExcelDictConvert.class) private Integer gender; @ExcelProperty(value = "余额",sort = 9) private Float balance; public static class TestConvert implements Converter { @Override public Class supportJavaTypeKey() { return Integer.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { return Integer.valueOf(cellData.getStringValue()); } @Override public WriteCellData convertToExcelData(Object value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { return new WriteCellData((String)value); } } } List testList = db.query(); HttpServletResponse response; EasyExcel.write() .file(response) // .file(response.getOutputStream()) .head(Test.class) .sheet() .doWrite(list); ``` ### 导入: #### bean类 ```$xslt @Setter @Getter @Entity @ExcelPropertyIgnoreUnannotated @ContentRowHeight(60) @ContentFontStyle(italic = BooleanEnum.FALSE,fontName = "Arial") @HeadRowHeight(40) @ContentStyle(locked=BooleanEnum.TRUE,wrapped=BooleanEnum.TRUE) public class Test { @ExcelProperty(value = "用户名",order = 10) private String username; @ExcelProperty(value = "年龄",converter = TestConvert.class) private Integer age; @ExcelDict({"0=男", "1=女"}) @ExcelProperty(value = "性别",convert = ExcelDictConvert.class) private Integer gender; @ExcelProperty(value = "余额",sort = 9) private Float balance; public static class TestConvert implements Converter { @Override public Class supportJavaTypeKey() { return Integer.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { return Integer.valueOf(cellData.getStringValue()); } @Override public WriteCellData convertToExcelData(Object value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { return new WriteCellData((String)value); } } } } EasyExcel.read() .file(inputStream) .head(Test.class) .registerReadListener(new ReadListener() { @Override public void invoke(Test test, AnalysisContext context) { testDao.save(TransformUtils.toPo(test, TestPo.class)); } }) .doReadAll(); ``` # oss文件上传 #### 目前仅支持阿里云/腾讯云/金山云/亚马逊/本地 ps: 阿里云/腾讯云/金山云配置类均使用AbstractOssConfig,本地配置使用AbstractLocalOssConfig #### example: 引入jar包 ```xslt com.efeichong oss-common ${version} ``` 配置yml,本地配置oss.local节点,S3配置oss.s3节点 ```xslt oss: #本地 local: #根路径 如:opt,D:// baseDir: c:// #展示的时候将以这个值替换根路径 例如: resource viewUrlPrefix: resource #上传返回的图片路径域名 例如: http://127.0.0.1:8188 showDomainUrl: http://127.0.0.1:8188 #s3 s3: #区域 region: #对外服务的访问域名 endPoint: #访问用户id 用于标识用户 accessKeyId: #访问密钥 用户用于加密签名字符串和OSS用来验证签名字符串的密钥 accessSecret: ``` 使用样例 ```$xslt @Autowired OssFactory ossFactory; LocalOssService ossService = ossFactory.create(OssType.LOCAL); //AlyOssService ossService = ossFactory.create(OssType.ALY); //String uploadUrl = alyOssService.getUploadUrl("bucketName", "folderName", "fileName"); String url = ossService.upload(file, folderName,"jpg"); ossService.delete("url1"); ossService.download(response,"url1"); ossService.batchDownload(response, Lists.newArrayList("url1","url2")); ossService.batchDelete(Lists.newArrayList("url1","url2")); ``` # 同步锁 - 线程同步锁 - 基于redis的分布式锁 ### 使用方式 #### 1. 在方法上面加 @Lock即可 key的要求: 写法1. theKey 写法2. ${com.test.MyConcatUtils.concat()} 这种写法要求 - 以${}做包裹 - 包路径必须正确,类必须为public修饰 - 方法必须是静态方法且为public修饰 - 该静态方法内如需用到spring装配请使用 {@link SpringUtils#getBean(String)} 例如: ```$xslt //@Lock(key = "key",lockType = LockType.THREAD_LOCK,retryCount = 1,lockExpireTime = 5*1000L,retryTimeInterval = 10L) @Lock(key = "${com.util.getMyKey()}",lockType = LockType.THREAD_LOCK,retryCount = 1,lockExpireTime = 5*1000L,retryTimeInterval = 10L) @Transactional @Override public void insertStudent(StudentVo studentVo){ studentDao.saveOrUpdate(TransformUtils.toPo(Student.class,studentVo)); } ``` #### 2. 手动使用锁 ```$xslt @Transactional @Override public void insertStudent(StudentVo studentVo){ EnhanceReentrantLock lock = new EnhanceReentrantLock(); // RedisReentrantLock lock = new RedisReentrantLock(); try { lock.lock("key"); studentDao.saveOrUpdate(TransformUtils.toPo(Student.class,studentVo)); } finally { lock.unlock("key"); } } ``` | 参数 | 值 | 说明 | 是否必须 | | --- |--- | --- | --- | | key |String| 唯一key,字符串或者用${}包裹的方法全路径静态方法 | 是| | lockType|LockType| 锁类型 redis,线程重入锁 默认使用 线程重入锁 |否| | lockExpireTime|long| 锁自动过期时长(大于0设置时间有效,小于等于0则为永不过期),过期自动释放,默认永不过期 单位:毫秒 |否| | retryCount|long| 自动重试次数,默认不重试 |否| | retryTimeInterval|long | 重试时间间隔,默认10毫秒,与重试次数配合使用 单位:毫秒 |否| ## websocket-common: ### websocket支持 注册消息监听,接收前端发送的消息 ```$xslt @Component @RequiredArgsConstructor public class InputChat implements MessageHandle { /** * 处理读取信息及返回信息 * * @param message * @return */ @Override public Map read(JSONObject message) { //to do something Map result = new HashMap(); result.put("code",1); result.put("msg","接收成功"); return result; } /** * 信息业务类型 * * @return */ @Override public String msgType() { return "CHAT_INPUT"; } ``` 发送消息给前端 ```$xslt @Autowired private WebSocketServer webSocketServer; webSocketServer.sendMessage(sid, "发送消息"); ``` ## util-common: ``` 基础工具类支持 ``` 1. 基于caffeine的对象缓存工具类 EntityCache 2. 缓存 caffeine 3. 基于cglib的动态代理工具 ProxyFactory,DynamicBean 4. 线程池 AsyncTaskExecutePool 5. 日期工具 DateUtils,joda 6. json,string,集合等处理工具 7. uuid IdGenerator和UUIDUtils