当前位置: 首页 > news >正文

如何设计制作一般企业网站/广告推广策划

如何设计制作一般企业网站,广告推广策划,国外的调查网站上做问卷,昭通建设网站往期热门文章:1、MyBatis批量插入几千条数据,请慎用foreach 2、有了 for (;;) ,为什么还需要while (true) ?到底哪个更快? 3、名企公开挂“加班真好”标语,员工称一年被免费“白嫖”600多小时!网…
往期热门文章:
1、MyBatis批量插入几千条数据,请慎用foreach
2、有了 for (;;) ,为什么还需要while (true) ?到底哪个更快?
3、名企公开挂“加班真好”标语,员工称一年被免费“白嫖”600多小时!网友看不下去了,稽查部门展开调查...
4、面试官:为什么 Java 不把基本类型放在堆中?我竟然答不上来。。
5、IDEA 注释模板这样搞!
来源:https://juejin.cn/post/7027733039299952676

对一个 Java 后端程序员来说,mybatishibernatedata-jdbc 等都是我们常用的 ORM 框架。它们有时候很好用,比如简单的 CRUD,事务的支持都非常棒。但有时候用起来也非常繁琐,比如接下来我们要聊到的一个常见的开发需求,最后本文会给出一个比直接使用这些 ORM 开发效率至少会提高 100 倍的方法(绝无夸张)。

09495eb6b93f1ec1969d5ca7e03373cd.png

| 首先数据库有两张表

用户表(user):(简单起见,假设只有 4 个字段)

字段名

类型

含义




id

bitint

用户 ID

name

varchar(45)

用户名

age

int

年龄

role_id

int

角色 ID

角色表(role):(简单起见,假设只有 2 个字段)

字段名

类型

含义




id

int

角色 ID

name

varchar(45)

角色名

|下来我们要实现一个用户查询的功能

这个查询有点复杂,它的要求如下:

  • 可按用户名字段查询,要求:

    • 可精确匹配(等于某个值)

    • 可全模糊匹配(包含给定的值)

    • 可后模糊查询(以...开头)

    • 可前模糊查询(以.. 结尾)

    • 可指定以上四种匹配是否可以忽略大小写

  • 可按年龄字段查询,要求:

    • 可精确匹配(等于某个年龄)

    • 可大于匹配(大于某个值)

    • 可小于匹配(小于某个值)

    • 可区间匹配(某个区间范围)

  • 可按角色ID查询,要求:精确匹配

  • 可按用户ID查询,要求:同年龄字段

  • 可指定只输出哪些列(例如,只查询 ID 与 用户名 列)

  • 支持分页(每次查询后,页面都要显示满足条件的用户总数)

  • 查询时可选择按  ID用户名 等任意字段排序

| 后端接口该怎么写呢?

试想一下,对于这种要求的查询,后端接口里的代码如果用 mybatishibernatedata-jdbc 直接来写的话,100 行代码 能实现吗?

反正我是没这个信心,算了,我还是直接坦白,面对这种需求后端如何 只用一行代码搞定 吧(有兴趣的同学可以 mybatis 等写个试试,最后可以对比一下)

| 手把手:只一行代码实现以上需求

首先,重点人物出场啦:Bean Searcher,  它就是专门来对付这种列表检索的,无论简单的还是复杂的,统统一行代码搞定!而且它还非常轻量,Jar 包体积仅不到 100KB,无第三方依赖。

假设我们项目使用的框架是 Spring Boot(当然 Bean Searcher 对框架没有要求,但在 Spring Boot 中使用更加方便)

添加依赖

Maven :

<dependency><groupId>com.ejlchina</groupId><artifactId>bean-searcher-boot-starter</artifactId><version>3.0.1</version>
</dependency>

Gradle :

implementation 'com.ejlchina:bean-searcher-boot-starter:3.0.1'

然后写个实体类来承载查询的结果

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") 
public class User {private Long id;  // 用户ID(u.id)private String name; // 用户名(u.name)private int age;  // 年龄(u.age)private int roleId;  // 角色ID(u.role_id)@DbField("r.name")  // 指明这个属性来自 role 表的 name 字段private int role;  // 角色名(r.name)// Getter and Setter ...
}

接着就可以写用户查询接口了

接口路径就叫 /user/index 吧:

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate MapSearcher mapSearcher;  // 注入检索器(由 bean-searcher-boot-starter 提供)@GetMapping("/index")public SearchResult<Map<String, Object>> index(HttpServletRequest request) {// 这里咱们只写一行代码return mapSearcher.search(User.class, MapUtils.flat(request.getParameterMap()));}}

上述代码中的 MapUtils 是 Bean Searcher 提供的一个工具类,MapUtils.flat(request.getParameterMap()) 只是为了把前端传来的请求参数统一收集起来,然后剩下的,就全部交给 MapSearcher 检索器了。

| 这样就完了?那我们来测一下这个接口,看看效果吧

(1)无参请求

  • GET /user/index

  • 返回结果:

{"dataList": [           // 用户列表,默认返回第 0 页,默认分页大小为 15 (可配置){ "id": 1, "name": "Jack", "age": 25, "roleId": 1, "role": "普通用户" },{ "id": 2, "name": "Tom", "age": 26, "roleId": 1, "role": "普通用户" },...],"totalCount": 100       // 用户总数
}

(2)分页请求(page | size)

  • GET /user/index? page = 2 & size = 10

  • 返回结果:结构同 (1)(只是每页 10 条,返回第 2 页)

参数名 size 和 page 可自定义, page 默认从 0 开始,同样可自定义,并且可与其它参数组合使用

(3)数据排序(sort | order)

  • GET /user/index? sort = age & order = desc

  • 返回结果:结构同 (1)(只是 dataList 数据列表以 age 字段降序输出)

参数名 sort 和 order 可自定义,可与其它参数组合使用

(4)指定(排除)字段(onlySelect | selectExclude)

  • GET /user/index? onlySelect = id,name,role

  • GET /user/index? selectExclude = age,roleId

  • 返回结果:( 列表只含 id,name 与 role 三个字段)

{"dataList": [           // 用户列表,默认返回第 0 页(只包含 id,name,role 字段){ "id": 1, "name": "Jack", "role": "普通用户" },{ "id": 2, "name": "Tom", "role": "普通用户" },...],"totalCount": 100       // 用户总数
}

参数名 onlySelect 和 selectExclude 可自定义,可与其它参数组合使用

(5)字段过滤(op = eq)

  • GET /user/index? age=20

  • GET /user/index? age=20 & age-op=eq

  • 返回结果:结构同 (1)(但只返回 age = 20 的数据)

参数 age-op = eq 表示 age 的 字段运算符 是 eqEqual 的缩写),表示参数 age 与参数值 20 之间的关系是 Equal,由于 Equal 是一个默认的关系,所以 age-op = eq 也可以省略

参数名 age-op 的后缀 -op 可自定义,且可与其它字段参数 和 上文所列的参数(分页、排序、指定字段)组合使用,下文所列的字段参数也是一样,不再复述。

(6)字段过滤(op = ne)

  • GET /user/index? age=20 & age-op=ne

  • 返回结果:结构同 (1)(但只返回 age != 20 的数据,ne 是 NotEqual 的缩写)

(7)字段过滤(op = ge)

  • GET /user/index? age=20 & age-op=ge

  • 返回结果:结构同 (1)(但只返回 age >= 20 的数据,ge 是 GreateEqual 的缩写)

(8)字段过滤(op = le)

  • GET /user/index? age=20 & age-op=le

  • 返回结果:结构同 (1)(但只返回 age <= 20 的数据,le 是 LessEqual 的缩写)

(9)字段过滤(op = gt)

  • GET /user/index? age=20 & age-op=gt

  • 返回结果:结构同 (1)(但只返回 age > 20 的数据,gt 是 GreateThan 的缩写)

(10)字段过滤(op = lt)

  • GET /user/index? age=20 & age-op=lt

  • 返回结果:结构同 (1)(但只返回 age < 20 的数据,lt 是 LessThan 的缩写)

(11)字段过滤(op = bt)

  • GET /user/index? age-0=20 & age-1=30 & age-op=bt

  • 返回结果:结构同 (1)(但只返回 20 <= age <= 30 的数据,bt 是 Between 的缩写)

参数 age-0 = 20 表示 age 的第 0 个参数值是 20。上述提到的 age = 20 实际上是 age-0 = 20 的简写形式。另:参数名 age-0 与 age-1 中的连字符 - 可自定义。

(12)字段过滤(op = mv)

  • GET /user/index? age-0=20 & age-1=30 & age-2=40 & age-op=mv

  • 返回结果:结构同 (1)(但只返回 age in (20, 30, 40) 的数据,mv 是 MultiValue 的缩写,表示有多个值的意思)

(13)字段过滤(op = in)

  • GET /user/index? name=Jack & name-op=in

  • 返回结果:结构同 (1)(但只返回 name 包含 Jack 的数据,in 是 Include 的缩写)

(14)字段过滤(op = sw)

  • GET /user/index? name=Jack & name-op=sw

  • 返回结果:结构同 (1)(但只返回 name 以 Jack 开头的数据,sw 是 StartWith 的缩写)

(15)字段过滤(op = ew)

  • GET /user/index? name=Jack & name-op=ew

  • 返回结果:结构同 (1)(但只返回 name 以 Jack 结尾的数据,ew 是 EndWith 的缩写)

(16)字段过滤(op = ey)

  • GET /user/index? name-op=ey

  • 返回结果:结构同 (1)(但只返回 name 为空 或为 null 的数据,ey 是 Empty 的缩写)

(17)字段过滤(op = ny)

  • GET /user/index? name-op=ny

  • 返回结果:结构同 (1)(但只返回 name 非空 的数据,ny 是 NotEmpty 的缩写)

(18)忽略大小写(ic = true)

  • GET /user/index? name=Jack & name-ic=true

  • 返回结果:结构同 (1)(但只返回 name 等于 Jack (忽略大小写) 的数据,ic 是 IgnoreCase 的缩写)

参数名 name-ic 中的后缀 -ic 可自定义,该参数可与其它的参数组合使用,比如这里检索的是 name 等于 Jack 时忽略大小写,但同样适用于检索 name 以 Jack 开头或结尾时忽略大小写。

当然,以上各种条件都可以组合,例如

查询 name 以 Jack (忽略大小写) 开头,且 roleId = 1,结果以 id 字段排序,每页加载 10 条,查询第 2 页:

  • GET /user/index? name=Jack & name-op=sw & name-ic=true & roleId=1 & sort=id & size=10 & page=2

  • 返回结果:结构同 (1)

OK,效果看完了,/user/index 接口里我们确实只写了一行代码,它便可以支持这么多种的检索方式,有没有觉得现在 你写的一行代码 就可以 干过别人的一百行 呢?

282086f6649faf5c35ef8917a24b8fe0.png

| Bean Searcher

本例中,我们只使用了 Bean Searcher 提供的 MapSearcher 检索器的一个 search 方法,其实,它有很多 search 方法。

检索方法

  • searchCount(Class<T> beanClass, Map<String, Object> params) 查询指定条件下的数据 总条数

  • searchSum(Class<T> beanClass, Map<String, Object> params, String field) 查询指定条件下的 某字段 的 统计值

  • searchSum(Class<T> beanClass, Map<String, Object> params, String[] fields) 查询指定条件下的 多字段 的 统计值

  • search(Class<T> beanClass, Map<String, Object> params) 分页 查询指定条件下数据 列表 与 总条数

  • search(Class<T> beanClass, Map<String, Object> params, String[] summaryFields) 同上 + 多字段 统计

  • searchFirst(Class<T> beanClass, Map<String, Object> params) 查询指定条件下的 第一条 数据

  • searchList(Class<T> beanClass, Map<String, Object> params) 分页 查询指定条件下数据 列表

  • searchAll(Class<T> beanClass, Map<String, Object> params) 查询指定条件下 所有 数据 列表

MapSearcher 与 BeanSearcher

另外,Bean Searcher 除了提供了 MapSearcher 检索器外,还提供了 BeanSearcher 检索器,它同样拥有 MapSearcher 拥有的方法,只是它返回的单条数据不是 Map,而是一个 泛型 对象。

参数构建工具

另外,如果你是在 Service 里使用 Bean Searcher,那么直接使用 Map<String, Object> 类型的参数可能不太优雅,为此, Bean Searcher 特意提供了一个参数构建工具。

例如,同样查询 name 以 Jack (忽略大小写) 开头,且 roleId = 1,结果以 id 字段排序,每页加载 10 条,加载第 2 页,使用参数构建器,代码可以这么写:

Map<String, Object> params = MapUtils.builder().field(User::getName, "Jack").op(Operator.StartWith).ic().field(User::getRoleId, 1).orderBy(User::getId, "asc").page(2, 10).build()
List<User> users = beanSearcher.searchList(User.class, params);

这里使用的是 BeanSearcher 检索器,以及它的 searchList(Class<T> beanClass, Map<String  Object> params) 方法。

运算符约束

上文我们看到,Bean Searcher 对实体类中的每一个字段,都直接支持了很多的检索方式。

但某同学:哎呀!检索方式太多了,我根本不需要这么多,我的数据量几十个亿呀,用户名字段的前模糊查询方式利用不到索引,万一把我的数据库查崩了怎么办呀?

好办,Bean Searcher 支持运算符的约束,实体类的用户名 name 字段只需要注解一下即可:

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") 
public class User {@DbField(onlyOn = {Operator.Equal, Operator.StartWith})private String name;// 为减少篇幅,省略其它字段...
}

如上,通过 @DbField 注解的 onlyOn 属性,指定这个用户名 name 只能适用与 精确匹配 和 后模糊查询,其它检索方式它将直接忽略。

上面的代码是限制了 name 只能有两种检索方式,如果再严格一点,只允许 精确匹配,那其实有两种写法。

(1)还是使用运算符约束:
@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") 
public class User {@DbField(onlyOn = Operator.Equal)private String name;// 为减少篇幅,省略其它字段...
}
(2)在 Controller 的接口方法里把运算符参数覆盖:
@GetMapping("/index")
public SearchResult<Map<String, Object>> index(HttpServletRequest request) {Map<String, Object> params = MapUtils.flatBuilder(request) // 收集前端传来的参数 .field(User::getName).op(Operator.Equal)   // 把 name 字段的运算符直接覆盖为 Equal.build()return mapSearcher.search(User.class, params);
}

条件约束

该同学又:哎呀!我的数据量还是很大,age 字段没有索引,根本不能参与 where 条件,一查就是一条 慢 SQL 啊!

不急,Bean Searcher 还支持条件的约束,让这个字段直接不能作为条件:

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") 
public class User {@DbField(conditional = false)private int age;// 为减少篇幅,省略其它字段...
}

如上,通过 @DbField 注解的 conditional 属性, 就直接不允许 age 字段参与条件了,无论前端怎么传参,Bean Searcher 都不搭理。

参数过滤器

该同学仍:哎呀!哎呀 ...

别怕! Bean Searcher 还支持配置全局参数过滤器,可自定义任何参数过滤规则,在 Spring Boot 项目中,只需要配置一个 Bean:

@Bean
public ParamFilter myParamFilter() {return new ParamFilter() {@Overridepublic <T> Map<String, Object> doFilter(BeanMeta<T> beanMeta, Map<String, Object> paraMap) {// beanMeta 是正在检索的实体类的元信息, paraMap 是当前的检索参数// TODO: 这里可以添加一下自定义的参数过滤规则return paraMap;      // 返回过滤后的检索参数}};
}

| 某同学问

参数咋这么怪,这么多呢,和前端有仇呀

  • 参数名是否奇怪,这其实看个人喜好,如果你不喜欢中划线 -,不喜欢 op

    ic 后缀,完全可以自定义,参考这篇文档:https://searcher.ejlchina.com/guide/latest/params.html#字段参数

  • 参数个数的多少,其实是和需求的复杂程度相关,如果需求很简单,其实很多参数没必要让前端传,后端直接塞进去就好,比如:name 只要求后模糊匹配,age 只要求区间匹配,那可以这样:

@GetMapping("/index")
public SearchResult<Map<String, Object>> index(HttpServletRequest request) {Map<String, Object> params = MapUtils.flatBuilder(request).field(User::getName).op(Operator.StartWith).field(User::getAge).op(Operator.Between).build()return mapSearcher.search(User.class, params);
}

这样前端就不用传 name-op 与 age-op 这两个参数了。

其实还有一种更简单的方法,那就是 运算符约束(当约束存在时,运算符默认就是 onlyOn 属性中指定的第一个值,前端可以省略不传):

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") 
public class User {@DbField(onlyOn = Operator.StartWith)private String name;@DbField(onlyOn = Operator.Between)private String age;// 为减少篇幅,省略其它字段...
}

入参是 request,我 swagger 文档不好渲染了呀

其实,Bean Searcher 的检索器只是需要一个 Map<String, Object> 类型的参数,至于这个参数是怎么来的,和 Bean Searcher 并没有直接关系。前文之所以从 request 里取,只是因为这样代码看起来简洁,如果你喜欢声明参数,完全可以把代码写成这样:

@GetMapping("/index")
public SearchResult<Map<String, Object>> index(Integer page, Integer size, String sort, String order, String name, Integer roleId,@RequestParam(value = "name-op", required = false) String name_op,@RequestParam(value = "name-ic", required = false) Boolean name_ic,@RequestParam(value = "age-0", required = false) Integer age_0,@RequestParam(value = "age-1", required = false) Integer age_1,@RequestParam(value = "age-op", required = false) String age_op) {Map<String, Object> params = MapUtils.builder().field(Employee::getName, name).op(name_op).ic(name_ic).field(Employee::getAge, age_0, age_1).op(age_op).field(Employee::getRoleId, roleId).orderBy(sort, order).page(page, size).build();return mapSearcher.search(User.class, params);
}

字段参数之间的关系都是 “且” 呀,那 “或” 呢?“且” “或” 任意组合呢?

上文所述的字段参数之间确是都是 "且" 的关系,至于 “或”,虽然这种使用场景不太多,但 Bean Searcher 也是支持的,详细可以参考这篇文章:

https://github.com/ejlchina/bean-searcher/issues/8

这里就不再复述了。

| 结语

本文介绍了 Bean Searcher 在复杂列表检索领域的超强能力,它之所以可以极大提高这类需求的研发效率,根本上归功于它 独创 的 动态字段运算符 与 多表映射机制,这是传统 ORM 框架所没有的。但由于篇幅所限,本文所述仍只是冰山一角,比如它还:

  • 支持 嵌入参数

  • 支持 字段转换器

  • 支持 Sql 拦截器

  • 支持 多数据源

  • 支持 自定义注解

  • 等等

| 项目 GitHub 地址

https://github.com/ejlchina/bean-searcher

最近热文阅读:

1、MyBatis批量插入几千条数据,请慎用foreach

2、有了 for (;;) ,为什么还需要while (true) ?到底哪个更快?

3、名企公开挂“加班真好”标语,员工称一年被免费“白嫖”600多小时!网友看不下去了,稽查部门展开调查...

4、面试官:为什么 Java 不把基本类型放在堆中?我竟然答不上来。。

5、IDEA 注释模板这样搞!

6、后端开挂:3行代码写出8个接口!

7、推荐一款可视化配置 Nginx 的神器

8、一款性能调优利器 — 火焰图

9、Redis 实现限流的三种方式

10、推荐 15 款常用开发工具

4cf7f5d2e4a9e65838d293b1d2bf0dbd.png

关注公众号,你想要的Java都在这里

http://www.lbrq.cn/news/843661.html

相关文章:

  • 东莞品牌营销型网站建设/指数函数和对数函数
  • 网站开发项目运营经理岗位职责/中国站长之家
  • 公司网站建设华为/2022年度关键词
  • 怎么制作网页调查问卷/seo黑帽是什么
  • 做网站一般图片的比例/网站排名查询工具
  • 网站建设在哪里备案/网络营销推广策划步骤
  • 制作视频的免费软件/seo查询工具网站
  • 阿里做网站/品牌策划方案案例
  • 石家庄网站建设电话/市场推广方案范文
  • 深圳住房和建设局网站认租申请/软文网站发布平台
  • 怎么给自己做网站吗/seo会被取代吗
  • 全屏响应式网站模板/个人开发app可以上架吗
  • 外贸大型门户网站建设/自己开网店怎么运营
  • 东莞市永铭装饰有限公司/搜索引擎优化服务公司哪家好
  • 菜单宣传网站怎么做/网络广告推广方法
  • wordpress网站如何添加栏目/模拟搜索点击软件
  • 南阳专业做网站公司/百度搜索指数排行
  • 做一个微网站平台/百度推广业务员
  • 有服务器了怎么做网站/apple私人免费网站怎么下载
  • 帝国cms建站实例教程/百度快照客服
  • 分类信息网站做淘客/seo资源网站排名
  • 营销型网站建设制作推广/市场调研方案
  • 在线做漫画网站/上海网络营销有限公司
  • 白帽网站/seo搜索引擎优化推广
  • 成都网站建设公司好做吗/搜索引擎营销的原理
  • 新郑整站优化/免费自己建网站
  • 我做的网站怎么是危险网站/网站seo关键词排名推广
  • 上海网站关键词排名优化报价/长沙百度快速排名
  • 网站建设企业站模板/个人网上卖货的平台
  • 个人博客网站制作教程/seo关键词排优化软件
  • MCP基础知识二(实战通信方式之Streamable HTTP)
  • 宝塔面板常见问题
  • leetGPU解题笔记(2)
  • ALB、NLB、CLB 负载均衡深度剖析
  • 从儿童涂鸦到想象力视频:AI如何重塑“亲子创作”市场?
  • linux-shell脚本