# jpaHelper **Repository Path**: hakiGit/jpaHelper ## Basic Information - **Project Name**: jpaHelper - **Description**: 本项目是基于spring-data-jpa的orm,主要特点是像mongodb一样使用sql数据库. - **Primary Language**: Java - **License**: MulanPSL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2021-05-30 - **Last Updated**: 2021-05-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # jpaHelper #### 介绍 本项目是基于spring-data-jpa的orm,主要特点是像mongodb一样使用sql数据库. 在敏捷开发中,最难以管理的业务关系是数据库的表结构,因为数据库表结构不可写成代码,无法使用版本管理工具进行迭代管理,每次新的需求来了以后,要使用各种手段修改各处的数据库表结构,开发数据库,测试数据库,正式数据库等等,而且要保证他们一致,否则上一个版本的代码运行在下一个版本的数据库上是会出现错误的. 传统关系型数据库,要修改表结构必须使用alter,create等语句。为了保证项目中测试数据库与正式数据库或其他数据库结构一致,有了flyway这种东西,但实际使用中依然不便。首先flyway以sql文件名版本号的形式来维护数据库版本,项目时间一长,flyway文件夹的sql文件数量会变得非常庞大,另外一点,两个开发者同时想要修改表结构时,极易产生版本冲突,两人可能在同一时间都提交了同一个版本号的sql文件,导致flyway执行出错,这种问题处理起来及其麻烦。另外如果一个开发人员本地代码的pojo类与数据库表字段对不上(已经被另外一个开发人员的flyway更新),执行指定字段的select或insert语句是会报错的,此时他只能等待另外一名开放人员将新版pojo类提交。 理想情况下,需求快速变化的敏捷开发应该使用mongodb这种文档性数据库,每个表(集合)的表结构都是动态的,可以插入任意结构的数据,本人另外一个项目mongoHelper就是为此而生的orm,如果能接受直接使用mongodb,可使用该项目: https://gitee.com/cym1102/mongoHelper jpaHelper为mongoHelper的兄弟项目,旨在为关系型数据库提供近似mongodb的使用体验.即开发过程中完全不用关心数据库结构,在任意一个空白或是有结构的数据库中,在项目启动的瞬间都可以立刻构建出与pojo类对应的数据库结构,可以立即开始进行业务开发.除了查询sql语句的执行效果,已经完全不必打开数据库客户端对数据库结构进行管理了. #### 软件架构 本项目只适用于springBoot项目,项目也依赖springBoot相关库,springMVC项目无法使用,另外项目依赖了hutool提供的诸多Util工具,让代码更简洁。 #### 安装教程 1. 引入maven库 ``` cn.craccd jpaHelper 0.2.8 ``` 2. 在有@Configuration的类上添加注解 ``` @ComponentScan("cn.cucc") @Configuration public class WebConfig { } ``` 3. 在application.yml文件中加入如下配置 ``` spring: datasource: #数据库配置(jpa会根据url协议自动判断数据库类型) url: jdbc:postgresql://xxx.xxx.xxx.xxx:5432/database username: postgres password: postgres jpa: properties: hibernate.globally_quoted_identifiers: true #对jpa sql关键字进行转义 hibernate.globally_quoted_identifiers_skip_column_definitions: true #不要对字段类型进行转义 hibernate: ddl-auto: update #自动更新数据库表,如不需要自动更新数据库,则取消此参数 ``` #### 使用说明 ###### 1. 基本操作 本orm会在容器中注入一个对象JpaHelper,这个对象拥有诸多单表查询功能,如下 - 按id删除:deleteById(String, Class) - 按条件删除:deleteByQuery(ConditionAndWrapper, Class) - 查询所有:findAll(Class) - 查询数量:findCount(Class) - 根据id查询:findById(String, Class) - 根据条件查询:findListByQuery(ConditionAndWrapper, Class) - 根据条件查询并分页:findPage(ConditionAndWrapper, Page, Class) - 插入:insert(Object) - 插入或更新:insertOrUpdate(Object) - 根据id更新:updateById(Object) - 根据id更新全部字段:updateAllColumnById(Object) 这个JpaHelper能够完成所有查询任务,插入和更新操作能够自动判断pojo的类型操作对应表,查询操作根据传入的Class进行对应表操作,本orm所有数据库操作都基于JpaHelper的功能,不用像mybatis一样,每个表都要建立一套Mapper,xml,Service,model,大大减少数据层的代码量。可以将JpaHelper直接注入到controller层,简单的操作直接调用JpaHelper进行操作,不需要调用service层。 而复杂的查询需要使用ConditionAndWrapper,将JpaHelper注入service,可以在service成构建查询条件。 **注意: 本orm的事务放在了controller层, 即每一个请求是一个事务,如果一个请求会修改数据库,请在controller的方法上加@Transactional,在service层加@Transactional无效** ###### 2. 复杂查询功能 本orm的查询功能都在JpaHelper的findByQuery,findPage方法中.使用ConditionAndWrapper和ConditionOrWrapper对象作为sql的拼接对象 ``` // 根据输入条件进行查询 public List search(String word, Integer type) { ConditionAndWrapper conditionAndWrapper = new ConditionAndWrapper(); if (StrUtil.isNotEmpty(word)) { conditionAndWrapper.and(new ConditionOrWrapper().like(User::getName, word).like(User::getPhone, word)); } if (type != null) { conditionAndWrapper.eq(User::getType, type); } List userList = jpaHelper.findListByQuery(conditionAndWrapper, User.class); return userList ; } ``` 以上代码组装了类似于select * from user where (name like '%xxx%' or phone like '%xxx%') and type = xxx的查询语句。 本项目不支持使用left join rigth join等连接查询,关系型数据库的连表查询能解决很多问题,但在大公司中已不再推荐使用,因为很难做数据库优化,数据量庞大时查询时间很慢而且很难进行优化。需要连表查询时,先查出对方id集,再使用in进行包含查询,可以很方便的走索引,而且分库的时候很容易修改。这样使用的话,实际是将关系型数据库用成了近似文档型数据库,表之间不再产生关联。 基于以上理念,本orm还提供了一些小功能用于完善这种多次连接查询,在jpaHelper中有以下方法 - 只查出表的id作为List返回:findIdsByQuery(ConditionAndWrapper conditionAndWrapper, Class clazz) - 只查出表的某个字段作为List返回:findPropertiesByQuery(ConditionAndWrapper conditionAndWrapper, Class documentClass, String property, Class propertyClass) 用法示例: ``` // 查出订单下的所有商品(OrderProduct.class为订单商品对照表) public List getProductList(String orderId) { List productIds = jpaHelper.findPropertiesByQuery(new ConditionAndWrapper().eq(OrderProduct::getOrderId, orderId), OrderProduct.class, OrderProduct::getProductId, String.class); return jpaHelper.findListByQuery(new ConditionAndWrapper().in(Product::getId, productIds), Product.class); } // 根据产品名查出所有订单 public PageResp search(PageReq pageReq, String keywords) { ConditionOrWrapper conditionOrWrapper = new ConditionOrWrapper(); if (StrUtil.isNotEmpty(keywords)) { List productIds = jpaHelper.findIdsByQuery(new ConditionAndWrapper().like(Product::getName, keywords), Product.class); List orderIds = jpaHelper.findPropertiesByQuery(new ConditionAndWrapper().in(OrderProduct::getProductId, productIds), OrderProduct.class, OrderProduct::getOrderId, String.class); conditionOrWrapper.in(Order::getId, orderIds); } PageResp page = jpaHelper.findPage(conditionOrWrapper, pageReq, Order.class); return page; } ``` ###### 3. 分页查询, 本orm提供一个PageResp类,包含total总记录数,pageSize每页记录数,pageNum起始页(从1开始), list结果列表四个属性,只要将包含pageNum和pageSize数据的PageReq对象传入findPage,即可查询出total,list的数据并自动返回到PageResp对象中。 ``` public PageResp search(PageReq pageReq, String word, Integer type) { ConditionAndWrapper conditionAndWrapper = new ConditionAndWrapper(); if (StrUtil.isNotEmpty(word)) { conditionAndWrapper.and(new ConditionOrWrapper().like(User::getName", word).like(User::getPhone, word)); } if (type != null) { conditionAndWrapper.eq(User::getType, type); } Sort sort = new Sort(User:getCreatTime, Direction.DESC); PageResp page = jpaHelper.findPage(conditionAndWrapper, sort, pageReq, User.class); return page; } ```