java规范

一、Java 项目规范

1.1、Java 项目命名规范

全部采用小写方式, 以中划线分隔。

正例:`mall-management-system / order-service-client / user-api`

反例:`mall_management-system / mallManagementSystem / orderServiceClient`

1.2、方法参数规范

无论是 controller,service,manager,dao 亦或是其他 class 的代码,每个方法最多 5 个参数,如果超出 5 个参数的话,要封装成 javabean 对象。

方便他人调用,降低出错几率。尤其是当参数是同一种类型,仅仅依靠顺序区分,稍有不慎便是灾难性后果,而且排查起来也极其恶心。

保持代码整洁、清晰度。当一个个方法里充斥着一堆堆参数的时候,再坚强的人,也会身心疲惫。
反例:

/**
* 使用证书加密数据工具方法
*
* @param param 参数
* @param password 加密密码
* @param priCert 私钥
* @param pubCert 公钥
* @param username 用户名
* @param ip ip地址
* @param userAgent 用户特征
* @return 返回加密后的字符串
  */
  public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert, String username, String ip, String userAgent){

}

1.3、代码目录结构

统一的目录结构是所有项目的基础。

src                               源码目录
|-- common                            各个项目的通用类库
|-- config                            项目的配置信息
|-- constant                          全局公共常量
|-- handler                           全局处理器
|-- interceptor                       全局连接器
|-- listener                          全局监听器
|-- module                            各个业务(方便将来拆成微服务)
|-- |--- employee                         员工模块
|-- |--- role                             角色模块
|-- |--- login                            登录模块
|-- third                             三方服务,比如redis, oss,微信sdk等等
|-- util                              全局工具类
|-- Application.java                  启动类

1.4、common 目录规范

common 目录用于存放各个项目通用的项目,但是又可以依照项目进行特定的修改。

src 源码目录
|-- common 各个项目的通用类库
|-- |--- anno          通用注解,比如权限,登录等等
|-- |--- constant      通用常量,比如 ResponseCodeConst
|-- |--- domain        全局的 javabean,比如 BaseEntity,PageParamDTO 等
|-- |--- exception     全局异常,如 BusinessException
|-- |--- json          json 类库,如 LongJsonDeserializer,LongJsonSerializer
|-- |--- swagger       swagger 文档
|-- |--- validator     适合各个项目的通用 validator,如 CheckEnum,CheckBigDecimal 等

1.5、module 目录规范

module 目录里写项目的各个业务,每个业务一个独立的顶级文件夹,在文件里进行 mvc 的相关划分。 其中,domain 包里存放 entity, dto, vo,bo 等 javabean 对象

src
|-- module                         所有业务模块
|-- |-- role                          角色模块
|-- |-- |--RoleController.java              controller
|-- |-- |--RoleConst.java                   role相关的常量
|-- |-- |--RoleService.java                 service
|-- |-- |--RoleDao.java                     dao
|-- |-- |--domain                           domain
|-- |-- |-- |-- RoleEntity.java                  表对应实体
|-- |-- |-- |-- RoleForm.java                     请求Form对象
|-- |-- |-- |-- RoleVO.java                      返回对象
|-- |-- employee                      员工模块
|-- |-- login                         登录模块
|-- |-- email                         邮件模块
|-- |-- ....                          其他

二、MVC 规范

2.1、整体分层

  • controller 层
  • service 层
  • manager 层
  • dao 层

2.2、 controller 层规范

1) 只允许在 method 上添加 RequestMapping 注解

只允许在 method 上添加 RequestMapping 注解,不允许加在 class 上(为了方便的查找 url,放到 class 上 url 不能一次性查找出来)

正例:

@RestController
public class DepartmentController {

    @GetMapping("/department/list")
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
        return departmentService.listDepartment();
    }

反例:

@RequestMapping ("/department")
public class DepartmentController {

    @GetMapping("/list")
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
        return departmentService.listDepartment();
    }

2)不推荐使用 restful 命名 url

不推荐使用 restful 命名 url, 只能使用 get/post 方法。url 命名遵循:/业务模块/子模块/动作 ;

其中 业务模块和子模块 使用 名字, 动作 使用动词; 原因:

虽然 restful 大法好,但是有时并不能一眼根据 url 看出来是什么操作,所以选择了后者,这个没有对与错,只有哪个更适合团队

正例:

GET  /department/getById      查询某个部门详细信息
POST /department/query         复杂查询
POST /department/add           添加部门
POST /department/update        更新部门
GET  /department/deleteById   删除部门

反例:

GET  /department/get/{id}      查询某个部门详细信息
POST /department/query         复杂查询
POST /department/add           添加部门
PUT /department/update        更新部门
DELETE  /department/delete/{id}   删除部门

3)swagger 接口注释必须加上后端作者
每个方法必须添加 swagger 文档注解 @ApiOperation ,并填写接口描述信息,描述最后必须加上接口的作者信息,格式如下: @author 智慧北洋

正例:

@ApiOperation("更新部门信息 @author 智慧北洋")
@PostMapping("/department/update")
public ResponseDTO<String> updateDepartment(@Valid @RequestBody DepartmentUpdateForm departmentUpdateForm) {
return departmentService.updateDepartment(departmentUpdateForm);
}

4)controller 每个方法要保持简洁

controller 在mvc中负责协同和委派业务,充当路由的角色,所以要保持代码少量和清晰,要做到如下要求:

不做任何的业务逻辑操作
不做任何的参数、业务校验,参数校验只允许使用@Valid 注解做简单的校验
不做任何的数据组合、拼装、赋值等操作
正例:

@ApiOperation("添加部门 @author 智慧北洋")
@PostMapping("/department/add")
public ResponseDTO<String> addDepartment(@Valid @RequestBody DepartmentAddForm departmentAddForm) {
return departmentService.addDepartment(departmentAddForm);
}

5)只能在 controller 层获取当前请求用户

只能在 controller 层获取当前请求用户,并传递给 service 层。

因为获取当前请求用户是从 ThreadLocal 里获取取的,在 service、manager、dao 层极有可能是其他非 request 线程调用,会出现 null 的情况,尽量避免

@ApiOperation("添加员工 @author 智慧北洋")
@PostMapping("/employee/add")
public ResponseDTO<String> addEmployee(@Valid @RequestBody EmployeeAddForm employeeAddForm) {
RequestUser requestUser = SmartRequestUtil.getRequestUser();
return employeeService.addEmployee(employeeAddForm, requestUser);
}

2.3、 service 层规范

1) 合理拆分 service 业务

不建议service文件行数太大 ,如果业务较大,请拆分为多个 service;

如订单业务,所有业务都写到 OrderService 中会导致文件过大,故需要进行拆分如下:

  • OrderQueryService 订单查询业务
  • OrderCreateService 订单新建业务
  • OrderDeliverService 订单发货业务
  • OrderValidatorService 订单验证业务

2) 谨慎使用 @Transactional 事务注解

谨慎使用 @Transactional 事务注解的使用,不要简单对 service 的方法添加个 @Transactional 注解就觉得万事大吉了。
应当合并对数据库的操作,尽量减少添加了@Transactional方法内的业务逻辑。
@Transactional 注解内的 rollbackFor 值必须使用异常 Exception.class

对于@Transactional 注解,当 spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,如果业务并没有进入到最终的 操作数据库环节,那么就没有必要获取连接并开启事务,应该直接将 connection 返回给数据库连接池,供其他使用(比较难以讲解清楚,如果不懂的话就主动去问)。

反例:

@Transactional(rollbackFor = Exception.class)
public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
// 验证 1
DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
if (departmentEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
// 验证 2
DepartmentEntity swapEntity = departmentDao.selectById(swapId);
if (swapEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
// 验证 3
Long count = employeeDao.countByDepartmentId(departmentId)
if (count != null && count > 0) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
}
// 操作数据库 4
Long departmentSort = departmentEntity.getSort();
departmentEntity.setSort(swapEntity.getSort());
departmentDao.updateById(departmentEntity);
swapEntity.setSort(departmentSort);
departmentDao.updateById(swapEntity);
return ResponseDTO.succ();
}

以上代码前三步都是使用 connection 进行验证操作,由于方法上有@Transactional 注解,所以这三个验证都是使用的同一个 connection。

若对于复杂业务、复杂的验证逻辑,会导致整个验证过程始终占用该 connection 连接,占用时间可能会很长,直至方法结束,connection 才会交还给数据库连接池。

对于复杂业务的不可预计的情况,长时间占用同一个 connection 连接不是好的事情,应该尽量缩短占用时间。

正例:

DepartmentService.java

    public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
        DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
        if (departmentEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        DepartmentEntity swapEntity = departmentDao.selectById(swapId);
        if (swapEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        Long count = employeeDao.countByDepartmentId(departmentId)
        if (count != null && count > 0) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
        }
        departmentManager.upOrDown(departmentSort,swapEntity);
        return ResponseDTO.succ();
    }


    DepartmentManager.java

    @Transactional(rollbackFor = Throwable.class)
    public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){
        Long departmentSort = departmentEntity.getSort();
        departmentEntity.setSort(swapEntity.getSort());
        departmentDao.updateById(departmentEntity);
        swapEntity.setSort(departmentSort);
        departmentDao.updateById(swapEntity);
    }

将数据在 service 层准备好,然后传递给 manager 层,由 manager 层添加@Transactional 进行数据库操作。

  • 以上是使用manager去处理解决的,其实也可以是使用spring的 TransactionTemplate 事务模板解决

3)需要注意的是:注解 @Transactional 事务在类的内部方法调用是不会生效的

反例:如果发生异常,saveData 方法上的事务注解并不会起作用

@Service
public class OrderService{

    public void createOrder(OrderAddForm addForm){
        this.saveData(addForm);
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveData(OrderAddForm addForm){
        orderDao.insert(addForm);
    }
}

Spring 采用动态代理(AOP)实现对 bean 的管理和切片,它为每个 class 生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。而在同一个 class 中,方法 A 调用方法 B,调用的是原对象的方法,而不通过代理对象。所以 Spring 无法拦截到这次调用,也就无法通过注解保证事务了。简单来说,在同一个类中的方法调用,不会被方法拦截器拦截到,因此事务不会起作用。

解决方案:

可以将方法放入另一个类,如新增 manager层,通过 spring 注入,这样符合了在对象之间调用的条件。
启动类添加 @EnableAspectJAutoProxy(exposeProxy = true),方法内使用AopContext.currentProxy()获得代理类,使用事务。

SpringBootApplication.java

@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}

OrderService.java

public void createOrder(OrderCreateDTO createDTO){
OrderService orderService = (OrderService)AopContext.currentProxy();
orderService.saveData(createDTO);
}

2.4、 manager 层规范

manager 层的作用(引自《阿里 java 手册》):

对第三方平台封装的层,预处理返回结果及转化异常信息;
对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;
与 DAO 层交互,对多个 DAO 的组合复用。

2.5、 dao 层规范

1)使用mybatis-plus的要求

  • 所有 Dao 继承自 BaseMapper
  • 禁止使用 Mybatis-plus 的 Wrapper 条件构建器;原因:1、SQL无法复用;2、排查如慢SQL的时候无法以搜索SQL的形式快速定位到代码;
  • 禁止直接在 mybatis xml 中写死常量,应从 dao 中传入到 xml 中
    正例: NoticeDao.java 常量在参数中传入到xml
public interface NoticeDao{

    Integer noticeCount(@Param("sendStatus") Integer sendStatus);

}

NoticeMapper.xml

<select id="noticeCount" resultType="integer">
select
count(1)
from t_notice
where
send_status = #{sendStatus}
</select>

反例:常量直接写死到 xml 中

@Select("select count(1)  from t_notice")
public interface NoticeDao{

    Integer noticeCount();

}

NoticeMapper.xml

<select id="noticeCount" resultType="integer">
select
count(1)
from t_notice
where
send_status = 0
</select>

4)连接join写法

建议在xml中的 join 关联写法使用表名的全称,而不是用别名,对于关联表太多的话,在xml格式中,其实很难记住 别名是什么意思!

反例:
t_notice 别名 tn,t_employee别名 e, 在xml中已经很难区分是什么意思了,索性不如使用全称

``xml
<select id="queryPage" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
SELECT tn.*,
e.actual_name AS createUserName
FROM t_notice tn
LEFT JOIN t_employee e ON tn.create_user_id = e.employee_id
<where>
tn.deleted_flag = #{queryForm.deletedFlag}
<if test="queryForm.keywords != null and queryForm.keywords != ''">
AND (INSTR(tn.notice_title,#{queryForm.keywords}) OR INSTR(e.actual_name,#{queryForm.keywords}))
</if>
<if test="queryForm.noticeType != null">
AND tn.notice_type = #{queryForm.noticeType}
</if>
<if test="queryForm.noticeBelongType != null">
AND tn.notice_belong_type = #{queryForm.noticeBelongType}
</if>
<if test="queryForm.startTime != null">
AND DATE_FORMAT(tn.publish_time, '%Y-%m-%d') >= #{queryForm.startTime}
</if>
<if test="queryForm.endTime != null">
AND DATE_FORMAT(tn.publish_time, '%Y-%m-%d') <= #{queryForm.endTime}
</if>
<if test="queryForm.disabledFlag != null">
AND tn.disabled_flag = #{queryForm.disabledFlag}
</if>
</where>
<if test="queryForm.sortItemList == null or queryForm.sortItemList.size == 0">
ORDER BY tn.top_flag DESC,tn.publish_time DESC
</if>
</select>

正确:使用全称

```xml
<select id="queryPage" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
SELECT t_notice.*,
t_employee.actual_name AS createUserName
FROM t_notice
LEFT JOIN t_employee  ON t_notice.create_user_id = t_employee.employee_id
<where>
t_notice.deleted_flag = #{queryForm.deletedFlag}
<if test="queryForm.keywords != null and queryForm.keywords != ''">
AND (INSTR(t_notice.notice_title,#{queryForm.keywords}) OR INSTR(t_employee.actual_name,#{queryForm.keywords}))
</if>
<if test="queryForm.noticeType != null">
AND t_notice.notice_type = #{queryForm.noticeType}
</if>
<if test="queryForm.noticeBelongType != null">
AND t_notice.notice_belong_type = #{queryForm.noticeBelongType}
</if>
<if test="queryForm.startTime != null">
AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') &gt;= #{queryForm.startTime}
</if>
<if test="queryForm.endTime != null">
AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') &lt;= #{queryForm.endTime}
</if>
<if test="queryForm.disabledFlag != null">
AND t_notice.disabled_flag = #{queryForm.disabledFlag}
</if>
</where>
<if test="queryForm.sortItemList == null or queryForm.sortItemList.size == 0">
ORDER BY t_notice.top_flag DESC,t_notice.publish_time DESC
</if>
</select>

2.6、 javabean 命名规范

1) javabean 的整体要求:

  • 不得有任何的业务逻辑或者计算
  • 基本数据类型必须使用包装类型(Integer, Double、Boolean 等)
  • 不允许有任何的默认值
  • 每个属性必须添加注释,并且必须使用多行注释。
  • 必须使用 lombok简化 getter/setter 方法
  • 建议对象使用 lombok@Builder@NoArgsConstructor,同时使用这两个注解,简化对象构造方法以及 set 方法。

2)javabean 名字划分:

  • XxxEntity 数据库持久对象
  • XxxVO 返回前端对象 (一些大厂用 Resp结尾,比如 XxxxResp)
  • XxxForm 前端请求对象 (一些大厂用 Req结尾,比如 XxxxReq)
  • XxxDTO 数据传输对象
  • XxxBO 内部处理对象

3)数据对象;XxxxEntity,要求:

  • Entity 为结尾(阿里是为 DO 为结尾)
  • Xxxx 与数据库表名保持一致
  • 类中字段要与数据库字段保持一致,不能缺失或者多余
  • 类中的每个字段添加注释,并与数据库注释保持一致
  • 不允许有组合
  • 项目内的日期类型必须统一,使用 java.time.LocalDateTime或者java.time.LocalDate`

4)请求对象;XxxxForm,要求:

  • 不可以继承自 Entity
  • Form 可以继承、组合其他 DTO,VO,BO 等对象
  • Form 只能用于前端、RPC 的请求参数

3)返回对象;XxxxVO,要求:

  • 不可继承自 Entity
  • VO 可以继承、组合其他 DTO,VO,BO 等对象
  • VO 只能用于返回前端、rpc 的业务数据封装对象

4)业务对象 BO,要求:

  • 不可以继承自 Entity
  • BO 对象只能用于 service,manager,dao 层,不得用于 controller

2.7、boolean 类型的属性命名规范

类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC 在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

这是阿里巴巴开发手册中的原文,团队的规定是:boolean 类型的类属性和数据表字段都统一使用 flag 结尾。虽然使用 isDeleted,is_deleted从字面语义上更直观,但是比起可能出现的潜在错误,这点牺牲还是值得的。

正例:

deletedFlag,deleted_flag,onlineFlag,online_flag

三、数据库 规范

3.1、数据库命名

全部采用小写方式, 以下划线分隔,并且区分是什么环境的数据库

正例:smart_admin_v2_dev / smart_admin_v2_prod / smart_admin_v2_test

反例:mall_management-system / mallManagementSystem / orderServiceClient

3.2、表命名

全部采用小写方式, 以下划线分隔,并且并且以 t_ 开头

比如:员工表:t_employee、部门表:t_department、配置表:t_config

3.3、建表规范

若需要的话,表必备三字段:[module]_id, create_time, update_time ,简单表除外:如 log表,不需要 update_time 等

[module]_id 字段 Long 类型,单表自增,自增长度为 1,module为业务名称,如:user_id、order_id 等
create_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP
update_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP

枚举类表字段注释需要将所有枚举含义进行注释

修改或增加字段的状态描述,必须要及时同步更新注释。如下表的 sync_status 字段 同步状态 0 未开始 1同步中 2同步成功 3失败

正例:

CREATE TABLE `t_change_data` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步状态 0 未开始 1同步中 2同步成功 3失败',
`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`change_data_id`)
)

反例:

CREATE TABLE `t_change_data` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步状态 ',
`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`change_data_id`)
)

3.4、索引规范

    1. 【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。

说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,

即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。

    1. 【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引。

说明:即使双表 join 也要注意表索引、SQL 性能。

    1. 【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。

说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。

    1. 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。

    1. 【推荐】如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。

正例:where a=? and b=? order by c; 索引:a_b_c

反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。

    1. 【推荐】利用覆盖索引来进行查询操作,避免回表。

说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。

正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用 explain 的结果,extra 列会出现:using index。

    1. 【推荐】利用延迟关联或者子查询优化超多分页场景。

说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL

改写。

正例:先快速定位需要获取的 id 段,然后再关联:

SELECT t1.* FROM 表 1 as t1, (select id from 表 1 where 条件 LIMIT 100000,20 ) as t2 where t1.id=t2.id
    1. 【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。

说明:

1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。

2) ref 指的是使用普通的索引(normal index)。

3) range 对索引进行范围检索。

反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range还低,与全表扫描是小巫见大巫。

    1. 【推荐】建组合索引的时候,区分度最高的在最左边。

正例:如果 where a=? and b=?,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。

说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么即使 c 的区分度更高,也必须把 d 放在索引的最前列,即建立组合索引 idx_d_c。

  • 10.【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
  • 11.【参考】创建索引时避免有如下极端误解:

1) 索引宁滥勿缺。认为一个查询就需要建一个索引。

2) 吝啬索引的创建。认为索引会消耗空间、严重拖慢记录的更新以及行的新增速度。

3) 抵制惟一索引。认为惟一索引一律需要在应用层通过“先查后插”方式解决。
```# 欢迎使用 Arya 在线 Markdown 编辑器

Arya,是一款基于 VueVditor,为未来而构建的在线 Markdown 编辑器;轻量且强大:内置粘贴 HTML 自动转换为 Markdown,支持流程图、甘特图、时序图、任务列表,可导出携带样式的图片、PDF、微信公众号特制的 HTML 等等。


如何使用

微注:清空目前这份默认文档,即处于可使用态。Arya 另一大优点在于:编辑内容只会在您本地进行保存,不会上传您的数据至服务器,绝不窥测用户个人隐私,可放心使用;Github 源码:markdown-online-editor,部分功能仍在开发🚧,敬请期待。

默认为所见即所得模式,可通过 ⌘-⇧-MCtrl-⇧-M)进行切换;或通过以下方式:

  • 所见即所得:⌘-⌥-7Ctrl-alt-7);
  • 即时渲染:⌘-⌥-8Ctrl-alt-8);
  • 分屏渲染:⌘-⌥-9Ctrl-alt-9);

PPT 预览

如果您用作 PPT 预览(入口在设置中),需要注意,这里暂还不能支持各种图表的渲染;您可以使用 --- 来定义水平方向上幻灯片,用 -- 来定义垂直幻灯片;更多设定可以参见 RevealJs 文档


什么是 Markdown

Markdown 是一种方便记忆、书写的纯文本标记语言,用户可以使用这些标记符号,以最小的输入代价,生成极富表现力的文档:譬如您正在阅读的这份文档。它使用简单的符号标记不同的标题,分割不同的段落,粗体斜体 或者超文本链接,更棒的是,它还可以:


1. 制作待办事宜 Todo 列表

  • 🎉 通常 Markdown 解析器自带的基本功能;
  • 🍀 支持流程图甘特图时序图任务列表
  • 🏁 支持粘贴 HTML 自动转换为 Markdown;
  • 💃🏻 支持插入原生 Emoji、设置常用表情列表;
  • 🚑 支持编辑内容保存本地存储,防止意外丢失;
  • 📝 支持实时预览,主窗口大小拖拽,字符计数;
  • 🛠 支持常用快捷键(Tab),及代码块添加复制
  • ✨ 支持导出携带样式的 PDF、PNG、JPEG 等;
  • ✨ 升级 Vditor,新增对 echarts 图表的支持;
  • 👏 支持检查并格式化 Markdown 语法,使其专业;
  • 🦑 支持五线谱、及部分站点、视频、音频解析
  • 🌟 增加对所见即所得编辑模式的支持(⌘-⇧-M);

2. 书写一个质能守恒公式[^LaTeX]

E=mc^2


3. 高亮一段代码[^code]

// 给页面里所有的 DOM 元素添加一个 1px 的描边(outline);
[].forEach.call($$("*"),function(a){
  a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16);
})

4. 高效绘制流程图

graph TD
    A[用户请求] --> B[语义解析]
    B --> C[RAG检索]
    
    C -->|✅ 知识库匹配| D[上下文增强]
    C -->|❌ 无匹配| E[任务分解]
    
    D --> E
    
    E --> F{工具选择}
    
    F -->|🛠️ 核心工具| G{基础操作}
    F -->|🔌 MCP扩展服务| H{MCP操作}
    
    G -->|✏️ 文件操作| I[读写/替换]
    G -->|🖥️ 系统命令执行| J[执行命令]
    G -->|🔍 代码分析| K[代码分析]
    
    H -->|⚙️ 使用MCP工具| L[使用MCP工具]
    H -->|📦 访问MCP资源| M[访问MCP资源]
    
    I --> N[结果验证]
    J --> N
    K --> N
    L --> N
    M --> N
    
    N --> O{完成判断}
    
    O -->|✅| P[提交最终结果]
    O -->|❌| E

5. 高效绘制序列图

sequenceDiagram
  participant Alice
  participant Bob
  Alice->John: Hello John, how are you?
  loop Healthcheck
      John->John: Fight against hypochondria
  end
  Note right of John: Rational thoughts <br/>prevail...
  John-->Alice: Great!
  John->Bob: How about you?
  Bob-->John: Jolly good!

6. 高效绘制甘特图

甘特图内在思想简单。基本是一条线条图,横轴表示时间,纵轴表示活动(项目),线条表示在整个期间上计划和实际的活动完成情况。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。

gantt
  title 项目开发流程
  section 项目确定
    需求分析       :a1, 2019-06-22, 3d
    可行性报告     :after a1, 5d
    概念验证       : 5d
  section 项目实施
    概要设计      :2019-07-05  , 5d
    详细设计      :2019-07-08, 10d
    编码          :2019-07-15, 10d
    测试          :2019-07-22, 5d
  section 发布验收
    发布: 2d
    验收: 3d

7. 支持图表

{
  "backgroundColor": "#212121",
  "title": {
    "text": "「晚晴幽草轩」访问来源",
    "subtext": "2019 年 6 月份",
    "x": "center",
    "textStyle": {
      "color": "#f2f2f2"
    }
  },
  "tooltip": {
    "trigger": "item",
    "formatter": "{a} <br/>{b} : {c} ({d}%)"
  },
  "legend": {
    "orient": "vertical",
    "left": "left",
    "data": [
      "搜索引擎",
      "直接访问",
      "推荐",
      "其他",
      "社交平台"
    ],
    "textStyle": {
      "color": "#f2f2f2"
    }
  },
  "series": [
    {
      "name": "访问来源",
      "type": "pie",
      "radius": "55%",
      "center": [
        "50%",
        "60%"
      ],
      "data": [
        {
          "value": 10440,
          "name": "搜索引擎",
          "itemStyle": {
            "color": "#ef4136"
          }
        },
        {
          "value": 4770,
          "name": "直接访问"
        },
        {
          "value": 2430,
          "name": "推荐"
        },
        {
          "value": 342,
          "name": "其他"
        },
        {
          "value": 18,
          "name": "社交平台"
        }
      ],
      "itemStyle": {
        "emphasis": {
          "shadowBlur": 10,
          "shadowOffsetX": 0,
          "shadowColor": "rgba(0, 0, 0, 0.5)"
        }
      }
    }
  ]
}

备注:上述 echarts 图表📈,其数据,须使用严格的 JSON 格式;您可使用 JSON.stringify(data),将对象传换从而得标准数据,即可正常使用。


8. 绘制表格

作品名称 在线地址 上线日期
逍遥自在轩 https://niceshare.site 2024-04-26
玉桃文飨轩 https://share.lovejade.cn 2022-08-26
缘知随心庭 https://fine.niceshare.site 2022-02-26
静轩之别苑 http://quickapp.lovejade.cn 2019-01-12
晚晴幽草轩 https://www.jeffjade.com 2014-09-20

9. 更详细语法说明

想要查看更详细的语法说明,可以参考这份 Markdown 资源列表,涵盖入门至进阶教程,以及资源、平台等信息,能让您对她有更深的认知。

总而言之,不同于其它所见即所得的编辑器:你只需使用键盘专注于书写文本内容,就可以生成印刷级的排版格式,省却在键盘和工具栏之间来回切换,调整内容和格式的麻烦。Markdown 在流畅的书写和印刷级的阅读体验之间找到了平衡。 目前它已经成为世界上最大的技术分享网站 GitHub 和 技术问答网站 StackOverFlow 的御用书写格式,而且越发流行,正在在向各行业渗透。

最新更新于 2025.04.16

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。