springboot实战之ORM整合(JPA篇)

  • 2019 年 10 月 4 日
  • 筆記

前言

1、什么是ORM

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。简单来说就是将数据库表与java实体对象做一个映射

2、ORM的优缺点

优点:符合面向对象编程;技术与业务解耦,开发时不需要关注数据库的连接与释放;

缺点:orm会牺牲程序的执行效率和会固定思维模式

3、orm主流框架

hibernate(jpa)、mybatis/mybatis-plus(半自动orm)。今天主要基于jpa规范再次封装抽象实现的SpringData JPA。在介绍SpringData JPA之前,先介绍一下jpa

什么是jpa

JPA是Java Persistence API的简称,中文名为Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

JPA包括以下3方面的内容:

(1)一套API标准。在javax.persistence的包下面,用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从烦琐的JDBC和SQL代码中解脱出来。

(2)面向对象的查询语言:Java Persistence QueryLanguage(JPQL)。这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

(3)ORM(object/relational metadata)元数据的映射。JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。

demo实战

通过demo示例可以了解或者掌握以下内容

  • 自动建表
  • 建表引擎改为InnoDB
  • 利用JpaSpecificationExecutor、JpaRepository来实现带复杂查询分页,以及常规增删改查
  • 重写SimpleJpaRepository的save方法,使其按需更新空值属性

1、pom.xml引入相应包

<dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-data-jpa</artifactId>      </dependency>     <dependency>        <groupId>com.alibaba</groupId>        <artifactId>druid-spring-boot-starter</artifactId>      </dependency>      <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>      </dependency>

2、application.yml配置

spring:    datasource:      name: druidDataSource      type: com.alibaba.druid.pool.DruidDataSource      druid:        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC        username: root        password: config.file=classpath:druid.properties        filters: stat,log4j,config        max-active: 100        initial-size: 1        max-wait: 60000        min-idle: 1        db-type: mysql        time-between-eviction-runs-millis: 60000        min-evictable-idle-time-millis: 300000        validation-query: select 'x'        test-while-idle: true        test-on-borrow: false        test-on-return: false        pool-prepared-statements: true        max-open-prepared-statements: 50        max-pool-prepared-statement-per-connection-size: 20        connection-properties: config.file=classpath:druid.properties   # 启用加密,配置公钥。        filter:          config:            enabled: true      jpa:      show-sql: true      hibernate:        ddl-auto: update      properties:        hibernate.format_sql: true      database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

jpa一些比较核心配置属性介绍

jpa.hibernate.ddl-auto参数的作用主要用于:自动创建|更新|验证数据库表结构。如果不是此方面的需求建议取值设为none

可选参数

  • create 启动时删数据库中的表,然后创建,退出时不删除数据表
  • create-drop 启动时删数据库中的表,然后创建,退出时删除数据表,如果表不存在报错
  • update 如果启动时表格式不一致则更新表,原有数据保留
  • validate 项目启动表结构进行校验 如果不一致则报错

spring.jpa.database-platform这个参数的主要用于指定默认的数据库存储引擎,在springboot2版本中,默认mysql数据库存储引擎的是MyISAM,通过把取值设置为org.hibernate.dialect.MySQL5InnoDBDialect,就可以把默认的存储引擎切换为InnoDB

3、创建entity

@Entity  @Table(name="order_log")  @AllArgsConstructor  @NoArgsConstructor  @Data  @Builder  @ToString(callSuper = true)  @EqualsAndHashCode(callSuper = true)  @IgnoreNullValue  public class OrderLog extends BaseEntity {      @Column(name="order_id")    private Long orderId;      @Column(name="order_content",length = 2000)    private String orderContent;      @Column(name="order_name")    private String orderName;      }    父类  @Data  @AllArgsConstructor  @NoArgsConstructor  @MappedSuperclass  public class BaseEntity {      @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;      @CreationTimestamp    @Column(name="create_date",updatable = false)    private Date createDate;      @UpdateTimestamp    @Column(name="update_date")    private Date updateDate;    }

entity注解解释

  • @Entity 声明类为实体或表。
  • @Table 声明表名
  • @Id 指定的类的属性,用于识别(一个表中的主键)。
  • @GeneratedValue 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值
  • @Column 指定持久属性栏属性。
  • @MappedSupperclass 用来申明一个超类,继承这个类的子类映射时要映射此类中的字段
  • @CreationTimestamp 数据库做插入时,自动填充时间
  • @UpdateTimestamp 数据库有更新时,自动更新时间

本例只用一个entity来演示,因此没有涉及到表与表的关联,常用表与表之间的关联注解如下

  • @JoinColumn 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。
  • @ManyToMany 定义了连接表之间的多对多一对多的关系。
  • @ManyToOne 定义了连接表之间的多对一的关系。
  • @OneToMany 定义了连接表之间存在一个一对多的关系。
  • @OneToOne 定义了连接表之间有一个一对一的关系。

4、创建Repository

通过继承JpaRepository可以实现增删改查,包括简单分页,通过继承JpaSpecificationExecutor可以实现复杂查询

public interface OrderLogRepository extends JpaSpecificationExecutor<OrderLog>,JpaRepository<OrderLog,Long> {    }

在使用Repository存在一个坑点,更新时,调用其提供的save方法会导致null属性覆盖到数据库。即如果要更新的bean中的字段,存在null值,原生的SimpleJpaRepository进行更新操作时,会把null值更新进数据库,而有时候业务上我们不需要这样,因此可以重写SimpleJpaRepository方法,其核心代码如下

/**       * 通用save方法 :新增/选择性更新       */      @Override      @Transactional      public <S extends T> S save(S entity) {          //获取ID          ID entityId = (ID) entityInformation.getId(entity);          Optional<T> optionalT;          if (entityId == null) {                //标记为新增数据              optionalT = Optional.empty();          } else {              //若ID非空 则查询最新数据              optionalT = findById(entityId);          }          //若根据ID查询结果为空          if (!optionalT.isPresent()) {              em.persist(entity);//新增              return entity;          } else {              if(entity.getClass().isAnnotationPresent(IgnoreNullValue.class)){                  IgnoreNullValue nullValue = entity.getClass().getAnnotation(IgnoreNullValue.class);                  if(nullValue.value()){                      //1.获取最新对象                      T target = optionalT.get();                      //2.将非空属性覆盖到最新对象                      BeanUtil.copyNotNUllProperties(entity,target);                      //3.更新非空属性                      em.merge(target);                      return (S) target;                  }else{                      em.merge(entity);                  }              }else{                  em.merge(entity);              }                return entity;          }      }

@IgnoreNullValue这个注解是用来指定是否要忽略空值字段。

在启动类上指定@EnableJpaRepositories注解,并将repositoryBaseClass设置为CustomSimpleJpaRepository,改成我们重写后的Repository,默认repositoryBaseClass为SimpleJpaRepository

@SpringBootApplication  @EnableJpaRepositories(basePackages = {"com.github.lybgeek.orm.jpa.repository"},repositoryBaseClass = CustomSimpleJpaRepository.class)  public class OrmApplication {      public static void main( String[] args ) {            SpringApplication.run(OrmApplication.class,args);      }  }

5、分页查询代码示例

public PageResult<OrderLog> pageOrderLogs(PageQuery<OrderLog> pageQuery) {        Sort sort = new Sort(Direction.DESC,"createDate");      Pageable pageable = PageRequest.of(pageQuery.getPageNo() - 1,pageQuery.getPageSize(),sort);        Specification<OrderLog> specification = (Specification<OrderLog>) (root, criteriaQuery, criteriaBuilder) -> {          OrderLog queryParams = pageQuery.getQueryParams();        if(queryParams != null){          List<Predicate> predicates = new ArrayList<>();          if(ObjectUtils.isNotEmpty(queryParams.getOrderId())){            Path<Long> orderId = root.get("orderId");            Predicate orderIdPredicate = criteriaBuilder.equal(orderId,queryParams.getOrderId());            predicates.add(orderIdPredicate);          }            if(StringUtils.isNotBlank(queryParams.getOrderName())){            Path<String> orderName = root.get("orderName");            Predicate orderNamePredicate = criteriaBuilder.like(orderName,"%"+queryParams.getOrderName()+"%");            predicates.add(orderNamePredicate);          }            if(CollectionUtils.isNotEmpty(predicates)){            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));          }          }            return null;      };        Page<OrderLog> orderLogPage = orderLogRepository.findAll(specification,pageable);      if(ObjectUtils.isNotEmpty(orderLogPage)){        return PageUtil.INSTANCE.getPage(orderLogPage);      }        return null;    }

文中相关概念引用文档

https://blog.csdn.net/u014131617/article/details/85813091 https://blog.csdn.net/xudailong_blog/article/details/84336629

总结

本文主要是介绍springdata jpa一些常规基本用法,只做入门,其具体更详细的内容,可以查看官网介绍

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

另外由于篇幅原因,其项目中包含的一些杂项诸如druid加密,flyway数据库版本管理,do和dto的相互转换,本文就不再论述,感兴趣的朋友可以查看我下边贴出来的demo链接。下篇会继续介绍mybatis、mybatisplus的基本使用

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-orm